Capítol 10

Codi net

En aquest capítol expliquem com pots escriure "codi net"

No sempre és bona idea fer... copia-enganxa

Aquí tens dos programes que fan exactament el mateix. Fixa't en les diferències:

✗ Codi brut
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
✓ Codi net
def avançar_i_recollir():
    move()
    if pearl_here():
        grab()

while front_is_clear():
    avançar_i_recollir()

El "codi brut" és molt llarg! I, a més, si vols canviar alguna cosa, és complicat i et pots equivocar en alguna línia.

El "codi net" és molt més senzill, i es llegeix millor. És molt fàcil fer algun canvi o millora.



Els noms que posem, els hem de pensar bé

Aquí tens dos programes que fan el mateix: en Karel recorre un corredor i recull cada perla que troba. Fixa't en la diferència... la veus?

✗ Noms que no expliquen res
def p():
    if pearl_here():
        grab()

while front_is_clear():
    move()
    p()
✓ Noms descriptius
def recollir_si_hi_ha_perla():
    if pearl_here():
        grab()

while front_is_clear():
    move()
    recollir_si_hi_ha_perla()

El títol p() , no vol dir res!
En canvi, el títol recollir_si_hi_ha_perla() ens explica molt bé què volem fer.






✗ Una funció fa dues coses
def anar_i_deixar_perla():
    while front_is_clear():
        move()
    drop()

anar_i_deixar_perla()
✓ Una funció, una responsabilitat
def avançar_fins_a_paret():
    while front_is_clear():
        move()

# Cada acció és independent i reutilitzable.
avançar_fins_a_paret()
drop()





⏱ A curt termini

  • Trobes errors més de pressa. Amb avançar_i_recollir() saps exactament on mirar. Amb f() i bb() has de llegir funció per funció fins a entendre qui fa què.
  • Modifiques en un sol lloc. Vols que en Karel deixi dues perles en lloc d'una? Canvies la definició de la funció i el canvi s'aplica arreu. Amb codi repetit, hauries de buscar i modificar cada còpia.
  • Rellegeixes el teu codi l'endemà. while front_is_clear(): avançar_i_recollir() s'entén d'un cop d'ull.

📅 A mig termini

  • Reutilitzes el que ja has escrit. Tens recollir_si_hi_ha_perla() d'un repte anterior? La copies directament al repte nou. Funciona sense errors, sense reescriure res.
  • Construeixes comportaments complexos. avançar_fins_a_paret() + recollir_si_hi_ha_perla() + turn_around() combinades donen un programa sofisticat llegible en tres línies.
  • El codi s'adapta sol. Si el món canvia —corredor més llarg, més perles— el teu codi net sovint no cal tocar-lo. El brut, sí.

🌍 A llarg termini

  • Pots compartir-lo. Un company llegeix while front_is_clear(): avançar_i_recollir() i l'entén sense que li expliquis res. El codi brut necessita una explicació verbal.
  • El codi es llegeix molt més que no s'escriu. En el món professional, el mateix codi el llegeixen desenes de persones durant anys. Escriure'l net és un acte de respecte cap als qui vindran després (incloent el tu del futur).
  • Col·laborar es torna possible. Sense noms clars i funcions petites, treballar en equip en un mateix projecte és un caos.

En resum, tres regles simples:

  • No dupliquis codi.
  • Posa noms que expliquin bé el que fas. avançar_i_recollir() sempre és millor que fer_cosa().
  • Cada funció, una idea. Quan escrius una funció, ha de ser per aconseguir un sol objectiu, i no per fer tres coses alhora.

Exercici. Netegem el codi

El codi de sota funciona, però és brut: copia el mateix bloc set vegades. La teva tasca és reescriure'l de manera neta usant una funció i un while. El resultat ha de ser el mateix: en Karel ha de recórrer tot el corredor i recollir totes les perles.

Codi brut que has de netejar:

# ✗ NO copiïs això — reescriu-lo net!
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()
move()
if pearl_here():
    grab()

Pista: crea una funció avançar_i_recollir() que faci el move() i el if pearl_here(): grab(). Després usa un while front_is_clear(): per cridar-la tantes vegades com calgui, sigui quin sigui el corredor.

EXERCICI 2 — Mig

Noms i estructura

En Karel ha de recórrer el corredor horitzontal, girar a la dreta i recollir la perla de la cantonada. El codi de sota ho fa, però té dos problemes: els noms de les funcions no expliquen res, i els moviments estan escrits un per un en lloc d'usar un while. Reescriu-lo net.

Codi brut que has de netejar:

# ✗ NO copiïs això — reescriu-lo net!
# Problema 1: f i g no diuen res sobre el que fan
# Problema 2: els tres move() s'haurien d'expressar amb while
def f():
    turn_left()
    turn_left()
    turn_left()

def g():
    if pearl_here():
        grab()

move()
move()
move()
f()
move()
g()

Pista: canvia els noms de f i g per noms que descriguin el que fan. Afegeix una funció avançar_fins_a_paret() que usi while front_is_clear(): move() en lloc dels tres move() explícits. Recorda que turn_right() existeix com a instrucció directa si vols simplificar el gir.

EXERCICI 3 — Difícil

Neteja total

En Karel ha de recórrer el corredor d'esquerra a dreta recollint les perles, girar i tornar deixant-les una a una. El codi de sota ho fa correctament, però és un desastre: sis funcions sense nom, dos bucles escrits com a repeticions explícites, i un gir amb dos turn_left() que es podria fer en una sola instrucció. Troba tots els problemes i reescriu el codi completament net.

Codi brut que has de netejar:

# ✗ NO copiïs això — reescriu-lo net!
def aa():
    move()

def bb():
    if pearl_here():
        grab()

def cc():
    aa()
    bb()

def dd():
    turn_left()
    turn_left()

def ee():
    if not bag_is_empty():
        drop()

def ff():
    aa()
    ee()

cc()
cc()
cc()
cc()
cc()
cc()
cc()
dd()
ff()
ff()
ff()
ff()
ff()
ff()
ff()

Set problemes a corregir:

  • Les funcions aa, bb, cc, dd, ee, ff no expliquen res amb els seus noms.
  • cc() fa dues coses alhora: avançar i recollir. Viola la regla «una funció, una idea».
  • ff() fa dues coses alhora: avançar i deixar una perla. Mateixa violació.
  • dd() usa dos turn_left() per fer mitja volta, quan existeix la instrucció turn_around().
  • Els set cc() consecutius s'han de substituir per un while front_is_clear():.
  • Els set ff() consecutius s'han de substituir per un while front_is_clear():.
  • Les funcions aa i bb gairebé no calen si defineixes bé les funcions compostes.