La lliçó anterior va acabar amb un programa que ja sap decidir, però que continua sent incapaç de fer el més natural del món: insistir. Quan la Marta escriu urgentissima on hi anava una prioritat, TascaFàcil interpreta el que pot i, si no ho entén, s'inventa un valor per defecte. El raonable seria tornar a preguntar. I per tornar a preguntar cal repetir.

Repetir és la segona gran estructura de control, i la que converteix un programa en una cosa que treballa de debò: recórrer dades, insistir fins a obtenir una resposta vàlida, acumular totals, calcular mitjanes. Sense bucles, un programa només pot fer tantes coses com línies tingui escrites; amb bucles, deu línies poden processar deu dades o deu mil.

En aquesta lliçó aprendràs les dues formes de repetir en Python —while i for—, quan fer servir cadascuna, els quatre patrons que apareixen una vegada i una altra al codi real, i com tancar per fi el cap solt que arrosseguem des del mòdul 2.

Contingut

  1. Repetir sense repetir codi
  2. El bucle while
  3. Bucles infinits i com sortir-ne
  4. El bucle for i la funció range()
  5. Recórrer una cadena caràcter a caràcter
  6. while contra for: quan fer servir cadascun
  7. Els quatre patrons clàssics
  8. TascaFàcil v0.5.2: insistir fins que la dada sigui vàlida
  9. Errors comuns i consells
  10. Exercicis
  11. Conclusió

  1. Repetir sense repetir codi

Imagina que la Marta vol veure el compte enrere dels tres dies que falten per lliurar el cartell de la fira del llibre. Amb el que sabies fins ara, l'única sortida era escriure tres print gairebé idèntics. Funciona per a tres dies; per a trenta, són trenta línies, i per a un número que l'usuari decideix mentre el programa s'executa, és senzillament impossible.

Un bucle (o iteració) ho resol: escrius el bloc una sola vegada i li dius al programa quantes vegades, o fins quan, l'ha d'executar. Cada passada del bloc s'anomena iteració.

Python ofereix dos bucles, i la diferència entre ells és només aquesta: while repeteix mentre una condició sigui vertadera, sense saber per endavant quantes voltes farà, i for repeteix per a cada element d'una seqüència, amb el nombre de voltes determinat abans de començar.

  1. El bucle while

La seva sintaxi és idèntica a la de l'if: paraula clau, condició, dos punts i bloc indentat de 4 espais. La diferència és que, en acabar el bloc, el flux torna a la condició en comptes de seguir endavant.

dies_restants = 3                           # 1. inicialitzacio

while dies_restants > 0:                    # 2. condicio de continuacio
    print(f"Queden {dies_restants} dies")   # 3. cos
    dies_restants -= 1                      # 4. actualitzacio

print("Termini exhaurit.")

S'imprimeix Queden 3 dies, Queden 2 dies, Queden 1 dies i, ja fora del bucle, Termini exhaurit.. Tot bucle while correcte té aquestes quatre peces, i convé aprendre-se-les com una llista de comprovació:

Peça Què fa Si falta
Inicialització Dona un valor inicial a la variable de control NameError: la variable no existeix
Condició de continuació S'avalua abans de cada volta SyntaxError
Cos La feina que es repeteix IndentationError
Actualització Modifica la variable per acostar-se a la sortida Bucle infinit

Fixa't en un detall important: la condició s'avalua abans de la primera volta. Si és falsa des del principi —per exemple, amb dies_restants = 0—, el cos no s'executa ni una sola vegada. Un while pot fer zero voltes, i això gairebé mai no és un error: sol ser exactament el que volem.

flowchart TD
    A["Inicialitzar variable"] --> B{"Condicio certa ?"}
    B -->|False| E["Continuar el programa"]
    B -->|True| C["Executar el cos"]
    C --> D["Actualitzar variable"]
    D --> B

La fletxa que torna de l'actualització a la condició és el que distingeix un bucle d'un if. És literalment un cicle.

  1. Bucles infinits i com sortir-ne

Si oblides l'actualització, la condició no canvia mai i el bucle no acaba mai:

dies_restants = 3
while dies_restants > 0:
    print(f"Queden {dies_restants} dies")    # falta dies_restants -= 1

El programa imprimeix Queden 3 dies per sempre, fins a omplir la pantalla. No és un error de sintaxi: Python no pot saber que t'has equivocat, perquè un bucle sense fi és perfectament legal (de fet, a la lliçó Menús interactius el farem servir a propòsit).

Com aturar-lo: prem Ctrl+C al terminal. Això interromp el programa i mostra un KeyboardInterrupt. No és una fallada del teu codi: és el senyal que has tallat tu l'execució.

Els tres descuits que els produeixen són gairebé sempre els mateixos: no actualitzar la variable de control dins del cos, actualitzar-la en la direcció equivocada (dies_restants += 1 quan la condició és > 0), o actualitzar-la fora del bucle, és a dir, sense la indentació correcta, amb la qual cosa només s'executa en acabar. Abans d'executar un while, fes-te aquesta pregunta: quina instrucció d'aquest cos farà que la condició acabi sent falsa? Si no la saps assenyalar amb el dit, tens un bucle infinit.

  1. El bucle for i la funció range()

El for repeteix un bloc per a cada element d'una seqüència. Quan el que vols és simplement repetir n vegades, la seqüència la genera range(), que produeix una successió de nombres enters.

for numero in range(3):
    print(f"Volta numero {numero}")      # imprimeix 0, despres 1, despres 2

La variable numero és la variable de control: Python la crea, li assigna el primer valor de la seqüència, executa el cos, li assigna el següent, i així fins a exhaurir-la. No cal inicialitzar-la ni actualitzar-la: d'això se n'encarrega el for, i per això un for mai no produeix un bucle infinit per descuit.

range() admet tres formes, i convé dominar-les totes tres:

Forma Genera Exemple Valors
range(n) De 0 a n−1 range(4) 0, 1, 2, 3
range(a, b) De a fins a b−1 range(1, 5) 1, 2, 3, 4
range(a, b, pas) De a a b−1 saltant de pas en pas range(0, 10, 3) 0, 3, 6, 9
range(a, b, -pas) Compte enrere range(3, 0, -1) 3, 2, 1

La regla que cal gravar-se: el límit superior mai no s'inclou. range(5) dona cinc nombres, però el 5 no hi és; és la font número u dels errors «d'un» (off-by-one) en principiants.

for dia in range(1, 6):                   # els 5 dies laborables
    print(f"Dia {dia} de la planificacio")

for dies_restants in range(3, 0, -1):     # compte enrere: 3, 2, 1
    print(f"Queden {dies_restants} dies")
flowchart TD
    A["range genera la sequencia"] --> B{"Queden elements ?"}
    B -->|False| E["Continuar el programa"]
    B -->|True| C["Assignar el seguent<br/>a la variable de control"]
    C --> D["Executar el cos"]
    D --> B

Compara'l amb el diagrama del while: l'estructura és la mateixa, però aquí no hi ha actualització manual perquè la mateixa seqüència decideix quan s'acaba.

  1. Recórrer una cadena caràcter a caràcter

range() no és l'única seqüència que un for pot recórrer. Una cadena de text també ho és: els seus elements són els seus caràcters, d'esquerra a dreta.

titol = "Cartell"
for lletra in titol:
    print(lletra, end=" ")       # C a r t e l l

Això obre la porta a analitzar text sense conèixer encara estructures de dades. Per exemple, comptar quants espais té un títol, que és una manera rudimentària de comptar paraules:

titol = "Cartell de la fira del llibre"
espais = 0

for caracter in titol:
    if caracter == " ":
        espais += 1

print(f"El titol te {espais + 1} paraules")   # 6 paraules

Fixa't en la combinació: un for que recorre, un if que decideix i un comptador que acumula. Aquesta tríada és el 80% del codi que escriuràs. Recórrer col·leccions de tasques amb for —una llista amb totes les tasques de l'estudi— és exactament la mateixa idea, i arribarà a Llistes i arrays.

  1. while contra for: quan fer servir cadascun

Els dos bucles poden simular l'altre, però cada problema té la seva forma natural.

while for
Es fa servir quan No saps quantes voltes faràs Coneixes el nombre de voltes o la seqüència
El governa Una condició Una seqüència
Variable de control La inicialitzes i actualitzes tu La gestiona Python
Risc de bucle infinit Sí, si oblides actualitzar Pràcticament cap
Exemple típic Demanar una dada fins que sigui vàlida Repetir un càlcul 12 vegades

La pregunta que ho resol gairebé sempre: puc saber el nombre de repeticions abans de començar? Si la resposta és sí, for. Si depèn d'alguna cosa que passarà dins del bucle —el que tecleja l'usuari, un càlcul que convergeix—, while.

  1. Els quatre patrons clàssics

Gairebé tot el que es fa amb bucles cau en un d'aquests quatre motlles. Tots quatre comparteixen la mateixa anatomia: una variable que s'inicialitza abans del bucle i es modifica a dins.

Comptador: compta quantes vegades passa una cosa.

tasques_altes = 0
for i in range(4):
    prioritat = input("Prioritat de la tasca: ").strip().lower()
    if prioritat == "alta":
        tasques_altes += 1
print(f"Hi ha {tasques_altes} tasques de prioritat alta")

Acumulador: suma els valors que va veient.

total_dies = 0
for i in range(3):
    total_dies += int(input("Dies estimats: "))
print(f"El lot suma {total_dies} jornades")

Amb l'acumulador i el comptador junts surt la mitjana, que és una divisió ordinària: total_dies / quantitat_tasques. Recorda de la lliçó 02-02 que / sempre retorna un float.

Màxim i mínim: busca el valor més gran o més petit.

major = 0                       # menor que qualsevol estimacio valida
for i in range(3):
    dies = int(input("Dies estimats: "))
    if dies > major:
        major = dies
print(f"La tasca mes llarga son {major} jornades")

La clau és a la inicialització: major = 0 funciona perquè cap estimació vàlida no és menor o igual que zero. Per al mínim el truc simètric no val (menor = 0 no seria superat mai), així que s'inicialitza amb un valor deliberadament alt, com menor = 9999, o amb la primera dada llegida.

Bandera booleana: recorda si una cosa ha passat almenys una vegada.

hi_ha_urgent = False
for i in range(3):
    prioritat = input("Prioritat de la tasca: ").strip().lower()
    if prioritat == "alta":
        hi_ha_urgent = True
if hi_ha_urgent:
    print("AVIS: el lot conte urgencies.")

La bandera s'inicialitza a False i només pot passar a True; mai no torna enrere. És el patró que respon a preguntes del tipus «n'hi ha alguna…?».

  1. TascaFàcil v0.5.2: insistir fins que la dada sigui vàlida

Ara sí. El bucle de validació és el patró que anem prometent des de la lliçó 02-04, i la seva forma és sempre la mateixa: llegir la dada abans del bucle, i repetir mentre no sigui vàlida.

prioritat = input("Prioritat (alta/mitjana/baixa): ").strip().lower()

while prioritat not in PRIORITATS:
    print(f"'{prioritat}' no es una prioritat valida.")
    prioritat = input("Prioritat (alta/mitjana/baixa): ").strip().lower()

Llegeix-ho a poc a poc, perquè té un parany clàssic: l'input() apareix dues vegades, una abans del bucle i una altra a dins. La primera lectura és necessària perquè la condició tingui alguna cosa a avaluar; la segona és l'actualització de què parlàvem a l'apartat 2. Si oblides la de dins, el valor no canvia mai i tens un bucle infinit que et repeteix el mateix retret per sempre. Amb aquesta peça, TascaFàcil ja no s'inventa res: pregunta fins que li donen una dada correcta. Aquesta és la versió 0.5.2 completa, on el missatge d'error va incorporat al mateix prompt de la segona lectura per no allargar el codi.

# tascafacil.py - Estudi Alba
# Versio 0.5.2: entrada insistent i resum del lot

AMPLE = 46
HORES_PER_JORNADA = 8
PRIORITATS = ("alta", "mitjana", "baixa")
EQUIP = ("Marta", "Luis", "Nuria")
DIES_MIN = 1
DIES_MAX = 365

# --- 1. Titol: insistir mentre estigui buit ---
titol = input("Titol de la tasca             : ").strip()
while titol == "":
    titol = input("Buit. Titol de la tasca       : ").strip()

# --- 2. Responsable: insistir mentre no sigui de l'equip ---
responsable = input("Responsable (Marta/Luis/Nuria): ").strip().capitalize()
while responsable not in EQUIP:
    responsable = input("No es de l'equip. Responsable : ").strip().capitalize()

# --- 3. Prioritat: insistir mentre no sigui al cataleg ---
prioritat = input("Prioritat (alta/mitjana/baixa): ").strip().lower()
while prioritat not in PRIORITATS:
    prioritat = input("No valida. Prioritat          : ").strip().lower()

# --- 4. Dies: insistir mentre no sigui un numero dins de l'interval ---
dies_text = input("Dies estimats                 : ").strip()
while not dies_text.isdigit() or not DIES_MIN <= int(dies_text) <= DIES_MAX:
    dies_text = input(f"Enter de {DIES_MIN} a {DIES_MAX}. Dies        : ").strip()
dies_estimats = int(dies_text)

# --- Fitxa de la tasca ---
hores_estimades = dies_estimats * HORES_PER_JORNADA
print("=" * AMPLE)
print(f"{'Titol':<16}{titol:>{AMPLE - 16}}")
print(f"{'Responsable':<16}{f'{responsable} ({prioritat})':>{AMPLE - 16}}")
print(f"{'Dies / hores':<16}{f'{dies_estimats} / {hores_estimades}':>{AMPLE - 16}}")
print("=" * AMPLE)

Observa la condició del quart bucle, que reuneix tot el del mòdul: not dies_text.isdigit() or not DIES_MIN <= int(dies_text) <= DIES_MAX. Gràcies al curtcircuit de l'or, si el text no és un número la segona part no s'avalua i l'int() no arriba mai a executar-se amb brossa. Una sola condició valida tipus i interval alhora.

Cap de les quatre entrades no pot provocar ja un valor inventat. Compara-ho amb la versió 0.5.1: allà el programa corregia; aquí exigeix. Un fragment de sessió:

Prioritat (alta/mitjana/baixa): urgentissima
No valida. Prioritat          : ALTA
Dies estimats                 : quatre
Enter de 1 a 365. Dies        : 4

El resum del lot

La Marta sol planificar diverses tasques de cop: sap quantes són i vol saber quantes jornades sumen i quina és la mitjana. Com que coneixem el nombre de voltes abans de començar, això és territori del for, amb tres dels patrons de l'apartat 7 alhora.

# --- Resum de planificacio del lot ---
quantitat_tasques = int(input("Quantes tasques planificaras? "))
total_jornades = 0
major = 0

for i in range(quantitat_tasques):
    dies = int(input(f"Jornades de la tasca {i + 1}: "))
    total_jornades += dies
    if dies > major:
        major = dies

mitjana = total_jornades / quantitat_tasques
print(f"Total del lot   : {total_jornades} jornades")
print(f"Mitjana per tasca: {mitjana:.1f} jornades")
print(f"La mes llarga   : {major} jornades")

Amb les jornades 4, 10 i 1, el resum surt Total del lot: 15 jornades, Mitjana per tasca: 5.0 jornades i La mes llarga: 10 jornades.

Tres detalls que mereixen atenció. L'i + 1 del missatge existeix perquè range() comença en 0 i les persones comptem des d'1. El :.1f és l'especificador d'f-string de la lliçó 02-03, necessari perquè / retorna un float amb massa decimals. I el més important: les dades de cada tasca es perden tan bon punt es llegeix la següent. Podem sumar-les i comparar-les al vol, però no desar-les: TascaFàcil continua tenint lloc per a una sola tasca. Per emmagatzemar-ne diverses calen llistes.

Errors Comuns i Consells

Oblidar l'actualització en un while. L'error clàssic i el que produeix bucles infinits. Abans d'executar, assenyala amb el dit la línia que farà falsa la condició.

Oblidar el segon input() en un bucle de validació. Si només llegeixes abans del while, el valor no canvia mai i el programa repeteix el mateix avís eternament. Ctrl+C i a revisar la indentació.

Comptar malament amb range(). range(5) dona 0, 1, 2, 3, 4: el límit superior no s'inclou. Si vols de l'1 al 5, escriu range(1, 6).

Inicialitzar l'acumulador dins del bucle. Posar total = 0 dins del cos el reinicia a cada volta, i al final val el mateix que l'última dada: la inicialització va sempre abans del bucle. I compte amb la variable de control d'un for: després de for i in range(3):, i val 2, no 3, i modificar-la dins del cos no afecta les voltes següents.

Consell: escriu primer la condició de sortida. Abans de teclejar el cos, pregunta't quan ha d'acabar el bucle. Un bucle sense una idea clara del seu final és un bucle infinit en potència.

Consell: prova els bucles amb zero voltes. Què passa si l'usuari diu que planificarà 0 tasques? El for no fa cap volta i la mitjana provoca una divisió per zero. Els casos límit (0 voltes, 1 volta) descobreixen la majoria de les fallades.

Exercicis

Exercici 1: Predir el resultat

Sense executar res, indica què imprimeix cada fragment i quantes voltes fa.

# a)
for i in range(2, 8, 2):
    print(i, end=" ")
# b)
n = 5
while n > 5:
    n -= 1
print("fi", n)
# c)
total = 0
for i in range(1, 5):
    total += i
print(total)

Exercici 2: Comptador de vocals del títol

Escriu comptar_vocals.py que demani un títol de tasca i, recorrent-lo caràcter a caràcter amb un for, compti quantes vocals té (normalitza a minúscules abans amb .lower() i fes servir in "aeiou"). Mostra el total i el percentatge de vocals sobre la longitud del títol, amb un decimal. Pista: len(titol) retorna el nombre de caràcters.

Exercici 3: Repartiment de jornades de l'equip

Escriu repartiment_equip.py que pregunti a la Marta quantes tasques vol repartir i, per a cadascuna, demani les jornades estimades (validant amb un while que sigui un enter entre 1 i 30) i el responsable (validant amb un altre while que sigui a EQUIP). En acabar ha de mostrar el total de jornades del lot, la mitjana per tasca i si hi ha alguna tasca de més de 15 jornades, fent servir una bandera booleana.

Solucions

Solució 1.

Sortida Voltes
a 2 4 6 3 — range(2, 8, 2) genera 2, 4 i 6; el 8 queda fora
b fi 5 0 — la condició 5 > 5 és falsa des del principi, així que el cos no s'executa mai
c 10 4 — acumula 1+2+3+4; el 5 no entra a range(1, 5)

El cas b és el que més sorprèn: un while pot fer zero voltes. I el c recorda que el límit superior de range() sempre queda exclòs.

Solució 2.

# comptar_vocals.py - Estudi Alba
titol = input("Titol de la tasca: ").strip()
while titol == "":
    titol = input("El titol no pot estar buit. Titol: ").strip()

vocals = 0
for caracter in titol.lower():
    if caracter in "aeiou":
        vocals += 1

percentatge = vocals / len(titol) * 100
print(f"'{titol}' te {len(titol)} caracters i {vocals} vocals ({percentatge:.1f}%)")

Dos apunts. El .lower() s'aplica sobre la cadena que recorre el for, no sobre cada caràcter: normalitzar una vegada és més barat i més clar que fer-ho a cada volta. I caracter in "aeiou" fa servir l'operador in de la lliçó 02-02, que sobre cadenes comprova si un text està contingut en un altre; amb un sol caràcter equival a preguntar si és una d'aquestes cinc lletres.

Solució 3.

# repartiment_equip.py - Estudi Alba
EQUIP = ("Marta", "Luis", "Nuria")
quantitat_tasques = int(input("Quantes tasques repartiras? "))
total_jornades = 0
hi_ha_tasca_llarga = False

for i in range(quantitat_tasques):
    print(f"--- Tasca {i + 1} ---")
    dies_text = input("Jornades estimades (1-30): ").strip()
    while not dies_text.isdigit() or not 1 <= int(dies_text) <= 30:
        dies_text = input("Ha de ser un enter d'1 a 30. Jornades: ").strip()
    responsable = input("Responsable (Marta/Luis/Nuria): ").strip().capitalize()
    while responsable not in EQUIP:
        responsable = input("No es de l'equip. Responsable: ").strip().capitalize()
    total_jornades += int(dies_text)
    if int(dies_text) > 15:
        hi_ha_tasca_llarga = True

mitjana = total_jornades / quantitat_tasques
print(f"Total: {total_jornades} jornades | Mitjana: {mitjana:.1f} per tasca")
if hi_ha_tasca_llarga:
    print("AVIS: hi ha tasques de mes de 15 jornades. Conve partir-les.")

Fixa't en l'estructura: un for que sap quantes voltes farà conté dos while que no ho saben. Aquesta combinació —for per al que és comptable, while per al que és insistent— és la que apareix al codi real. I la bandera hi_ha_tasca_llarga es declara abans del for, perquè ha de sobreviure a totes les voltes; si fos a dins, es reiniciaria a cada tasca i al final només reflectiria l'última.

Conclusió

Ja saps repetir. Coneixes el bucle while amb les seves quatre peces —inicialització, condició de continuació, cos i actualització—, saps que la condició s'avalua abans de cada volta (i que per això un while pot fer zero voltes), i saps què és un bucle infinit, per què passa i que se'n surt amb Ctrl+C. Coneixes el bucle for amb les tres formes de range() —recordant que el límit superior mai no s'inclou— i la seva capacitat de recórrer una cadena caràcter a caràcter. I tens el criteri per triar: si pots comptar les voltes abans de començar, for; si depenen del que passi a dins, while.

Tens a més els quatre patrons que resolen la majoria dels problemes amb bucles: comptador, acumulador, màxim/mínim i bandera booleana, tots amb la mateixa anatomia de variable inicialitzada fora i modificada a dins. I TascaFàcil, en la seva versió 0.5.2, ha tancat per fi el cap solt que arrossegàvem des del mòdul 2: ja no corregeix ni s'inventa dades, insisteix fins que la Marta escriu una cosa vàlida, amb un bucle de validació per cada camp; a més sap resumir un lot de tasques —total, mitjana i màxim— encara que no pugui desar-les.

Queden caps, i són els que obren la lliçó següent. Si la Marta s'equivoca cinc vegades seguides, el programa la interroga cinc vegades sense pietat: no hi ha manera de rendir-se després d'un nombre raonable d'intents, ni de saltar-se una volta concreta, ni de tallar un bucle a mitja execució del cos quan ja se sap la resposta. Tampoc no sabem encara combinar dos bucles, un dins de l'altre, per recórrer una graella de dies per persona. Tot això és control fi del flux, i és el que aprendràs a Estructures de control avançades: break, continue, la sorprenent clàusula else dels bucles, els bucles imbricats i la sentència match per classificar valors amb més elegància que una cadena d'elif.

© Copyright 2026. Tots els drets reservats