Els bucles de la lliçó anterior tenen una virtut i un defecte, i són el mateix: són tot o res. Un while insisteix fins que la condició es torna falsa, costi el que costi; un for recorre la seva seqüència sencera encara que a la primera volta ja sàpiga la resposta. Si la Marta s'equivoca cinc vegades seguides en escriure la prioritat, TascaFàcil la interroga cinc vegades sense la menor compassió: no sap rendir-se.
Aquesta lliçó afegeix el control fi del flux: instruccions que tallen un bucle a mig camí, que se salten una volta concreta, que distingeixen entre «he acabat de cercar i ho he trobat» i «he acabat de cercar i no hi era», i que combinen dos bucles un dins de l'altre. Tanquem amb match, la sentència que Python va incorporar a la versió 3.10 per classificar valors amb més elegància que una cadena d'elif. Són eines potents i, precisament per això, fàcils de fer servir malament: un break col·locat sense criteri converteix un bucle llegible en un laberint, així que aprendrem també quan no utilitzar-les.
Contingut
break: sortir del bucle abans d'horacontinue: saltar-se una volta- La clàusula
elsedels bucles - Bucles imbricats
- Sentinelles i banderes per sortir de bucles imbricats
match/case: classificar amb elegància- Bones pràctiques del control de flux
- TascaFàcil v0.5.3: tres intents i classificació amb
match - Errors comuns i consells
- Exercicis
- Conclusió
break: sortir del bucle abans d'hora
break: sortir del bucle abans d'horabreak interromp el bucle immediatament. No acaba la volta en curs ni torna a avaluar la condició: abandona el bucle i el programa continua a la primera instrucció que hi hagi després.
for i in range(5):
resposta = input(f"Tasca {i + 1} (Intro buit per acabar): ").strip()
if resposta == "":
print("Registre interromput per l'usuari.")
break
print(f"Registrada: {resposta}")
print("Fi del registre.")Si l'usuari prem Intro a la tercera tasca, les voltes quarta i cinquena no s'executen. El bucle estava preparat per a cinc iteracions i n'ha fet tres.
flowchart TD
A["Inici de la volta"] --> B{"Condicio de break ?"}
B -->|True| F["Sortir del bucle"]
B -->|False| C{"Condicio de continue ?"}
C -->|True| A
C -->|False| D["Resta del cos"]
D --> A
F --> G["Instruccio posterior al bucle"]
L'ús més freqüent de break és el bucle infinit controlat: while True: fa voltes per sempre i l'única sortida és un break dins del cos. Sembla temerari, però és un patró perfectament respectable quan la condició de sortida només es pot avaluar a mitja volta, és a dir, després d'haver llegit una dada.
while True:
dies_text = input("Dies estimats (0 per sortir): ").strip()
if dies_text == "0":
break
if dies_text.isdigit():
print(f"Anotades {dies_text} jornades.")Sobre aquest esquelet es construeix el bucle principal d'una aplicació, que és justament el tema de Menús interactius.
continue: saltar-se una volta
continue: saltar-se una voltacontinue no surt del bucle: abandona la volta actual i salta directament a la següent. Tot el que hi hagués després d'ell al cos s'ignora, només en aquesta iteració.
total_jornades = 0
for i in range(4):
dies_text = input(f"Jornades de la tasca {i + 1}: ").strip()
if not dies_text.isdigit():
print(" Dada no numerica: tasca descartada del total.")
continue
total_jornades += int(dies_text)
print(f" Acumulat: {total_jornades} jornades")
print(f"Total valid: {total_jornades} jornades")Amb les entrades 4, vuit, 2 i 1, la segona volta imprimeix l'avís i salta; les altres tres acumulen. El total és 7.
continue serveix per descartar casos al principi del cos i deixar la resta del codi sense indentar. És l'equivalent en bucles de les clàusules de guarda que vas veure a 03-01. L'alternativa sense continue seria envoltar tot el cos en un if, cosa que funciona igual però afegeix un nivell d'indentació a cada línia.
break |
continue |
|
|---|---|---|
| Què fa | Acaba el bucle sencer | Acaba només la volta actual |
| On salta | A la instrucció posterior al bucle | A la iteració següent |
| Ús típic | Ja tinc el que buscava; cancel·lar | Descartar un element i continuar |
- La clàusula
else dels bucles
else dels buclesAquesta és una peculiaritat de Python que sorprèn fins i tot programadors amb experiència en altres llenguatges: tant for com while admeten un else final. I no significa el que sembla.
L'else d'un bucle s'executa si el bucle ha acabat de manera natural, és a dir, exhaurint la seqüència (for) o tornant-se falsa la condició (while); no s'executa si el bucle ha acabat per un break. La manera més fàcil de recordar-ho és llegir-lo com un else de break: «si el bucle ha acabat sense trencar-se, fes això». El seu cas d'ús canònic és la cerca que no ha trobat res:
EQUIP = ("Marta", "Luis", "Nuria")
cercat = "Nuria"
for i in range(3):
responsable = input(f"Responsable de la tasca {i + 1}: ").strip().capitalize()
if responsable == cercat:
print(f"Trobada una tasca de {cercat} a la posicio {i + 1}.")
break
else:
print(f"Cap de les 3 tasques no es de {cercat}.")Si en alguna volta apareix Nuria, el break talla el bucle i l'else no s'executa. Si s'exhaureixen les tres voltes sense trobar-la, el bucle acaba de manera natural i l'else avisa que la cerca ha estat infructuosa.
Sense aquesta clàusula caldria recórrer a una bandera: inicialitzar trobada = False, posar-la a True al costat del break, i comprovar if not trobada: en sortir del bucle. Les dues versions són correctes: la de l'else és més curta, la de la bandera és més explícita i s'entén sense conèixer aquesta peculiaritat. Molts equips prefereixen la segona precisament per això; el for...else és una d'aquelles construccions que cal saber llegir encara que decideixis no escriure-la.
El while...else funciona igual i és encara menys freqüent: s'executa si el while ha acabat perquè la seva condició s'ha tornat falsa, no si ha sortit per break. El farem servir a TascaFàcil per al límit d'intents.
- Bucles imbricats
Un bucle pot contenir-ne un altre. L'interior s'executa complet a cada volta de l'exterior, així que el nombre total d'iteracions és el producte de tots dos.
for taula in range(2, 4):
for multiplicador in range(1, 4):
print(f"{taula} x {multiplicador} = {taula * multiplicador}")
print("---")S'imprimeixen les tres multiplicacions de la taula del 2, un separador, les tres de la taula del 3 i un altre separador: sis voltes del bucle interior en dues de l'exterior. I fixa't en el print("---"): està indentat al nivell del bucle exterior, així que s'executa una vegada per taula, no una vegada per multiplicació. En els bucles imbricats, la indentació ho és tot.
El cas realista per a Estudi Alba és una graella de dies per persona: la planificació setmanal de l'equip.
EQUIP = ("Marta", "Luis", "Nuria")
for dia in range(1, 6):
print(f"--- Dia {dia} ---")
for posicio in range(3):
persona = EQUIP[posicio]
hores = input(f"Hores de {persona}: ").strip()
print(f" {persona}: {hores} h")Aquí EQUIP[posicio] accedeix a l'element que hi ha en aquesta posició de la tupla, comptant des de 0; és el primer cop que ho fem i ho veurem a fons a Llistes i arrays. El bucle exterior fa 5 voltes i l'interior 3 a cadascuna: 15 preguntes en total.
I aquí hi ha el cost d'imbricar. Amb un bucle, doblar les dades dobla la feina; amb dos bucles imbricats, doblar les dades la quadruplica, perquè el total és el producte. Cinc dies per tres persones són 15 voltes, però 100 dies per 100 persones són 10.000. Aquesta relació entre la mida del problema i la feina que costa s'anomena complexitat, i s'estudia a Eficiència i notació Big-O.
- Sentinelles i banderes per sortir de bucles imbricats
Aquí apareix una limitació important: break només trenca el bucle en què es troba. Si ets dins del bucle interior i vols abandonar-los tots dos, un sol break no n'hi ha prou: surt de l'interior i l'exterior continua fent voltes. La solució habitual és una bandera que el bucle exterior consulta:
EQUIP = ("Marta", "Luis", "Nuria")
cancellat = False
for dia in range(1, 6):
for posicio in range(3):
hores = input(f"Hores de {EQUIP[posicio]} el dia {dia} (x per sortir): ").strip()
if hores.lower() == "x":
cancellat = True
break # surt del bucle interior
if cancellat:
print("Planificacio cancellada.")
break # surt del bucle exteriorEl mecanisme té tres peces: la bandera s'inicialitza a False abans de tot, es posa a True just abans del break interior, i el bucle exterior la comprova tot just en acabar l'interior. La "x" que tecleja l'usuari és el sentinella: un valor convingut que no és una dada vàlida i que té com a única funció assenyalar el final.
Un sentinella ha de complir dues condicions: no pot confondre's amb una dada legítima (si les hores poden ser 0, el 0 no serveix com a sentinella) i ha d'estar anunciat al missatge que veu l'usuari. Un sentinella secret és una dada perduda.
match / case: classificar amb elegància
match / case: classificar amb elegànciaDes de Python 3.10 existeix match, pensada per al cas concretíssim de comparar un valor contra diverses alternatives. És exactament el que fa una cadena d'elif amb el mateix == repetit, però es llegeix molt millor.
prioritat = "mitjana"
match prioritat:
case "alta":
missatge = "Termini: avui mateix."
case "mitjana":
missatge = "Termini: aquesta setmana."
case "baixa":
missatge = "Termini: quan hi hagi lloc."
case _:
missatge = "Prioritat desconeguda."
print(missatge) # Termini: aquesta setmana.Les regles són quatre. match valor: obre l'estructura amb el valor que es compararà; cada case proposa un patró i s'executa només el primer que coincideixi; case _: és el cas per defecte, equivalent a l'else, perquè el guió baix significa «qualsevol cosa»; i, acabat el case triat, el flux surt del match, sense necessitat d'escriure cap break com exigeixen C o Java.
Un case pot recollir diverses alternatives amb la barra vertical |, que aquí es llegeix «o»:
match prioritat:
case "alta" | "urgent" | "critica":
missatge = "Termini: avui mateix."
case "mitjana" | "normal":
missatge = "Termini: aquesta setmana."
case _:
missatge = "Termini: quan hi hagi lloc."Aquesta sola línia substitueix if prioritat == "alta" or prioritat == "urgent" or prioritat == "critica":, i fa evident que les tres paraules són sinònims del mateix.
if / elif |
match / case |
|
|---|---|---|
| Compara | Qualsevol condició booleana | Un valor contra patrons |
Intervals i comparacions (dies > 15) |
Sí | No de manera natural |
| Diverses alternatives per branca | in (...) o diversos or |
Patró amb ` |
| Cas per defecte | else |
case _ |
| Versió mínima de Python | Qualsevol | 3.10 |
La regla de tria és simple: si compares un valor contra una llista de possibilitats concretes, match; si les condicions són càlculs o intervals (dies > 15, hores <= 40), if / elif. I si el teu codi ha de funcionar en Python 3.9 o anterior, match no existeix.
- Bones pràctiques del control de flux
Aquestes eines es paguen en llegibilitat, així que convé un codi de conducta.
- Res de
breakgratuït. Si la condició de sortida cap a la capçalera delwhile, escriu-la allà:while intents < 3:és més honest quewhile True:amb unbreakamagat vint línies més avall. I un sol punt de sortida per bucle sempre que es pugui, perquè tresbreakrepartits obliguen a llegir el cos sencer per saber quan acaba. - Sempre un límit d'intents. Un bucle que pregunta fins a obtenir una dada vàlida pot preguntar eternament. Tres intents i una sortida digna és millor experiència que un interrogatori infinit.
- Compte amb imbricar. Dos nivells són manejables; tres, sospitosos. A partir d'aquí, replanteja el problema.
- TascaFàcil v0.5.3: tres intents i classificació amb
match
matchLa versió 0.5.2 insistia indefinidament. Ara apliquem la quarta bona pràctica: tres intents i ens rendim amb un valor per defecte anunciat, no inventat en secret. I la classificació de la prioritat passa a un match.
# tascafacil.py - Estudi Alba
# Versio 0.5.3: limit d'intents i classificacio amb match
AMPLE = 46
MAX_INTENTS = 3
PRIORITATS = ("alta", "mitjana", "baixa")
EQUIP = ("Marta", "Luis", "Nuria")
titol = input("Titol de la tasca : ").strip()
titol = titol if titol != "" else "(sense titol)"
responsable = input("Responsable (Marta/Luis/Nuria): ").strip().capitalize()
while responsable not in EQUIP:
responsable = input("No es de l'equip. Responsable : ").strip().capitalize()
# --- Prioritat: tres intents i ens rendim ---
intents = 0
prioritat = ""
while intents < MAX_INTENTS:
prioritat = input(f"Prioritat (alta/mitjana/baixa) [{intents + 1}/{MAX_INTENTS}]: ").strip().lower()
if prioritat in PRIORITATS:
break
print(f" '{prioritat}' no es valida.")
intents += 1
else:
prioritat = "mitjana"
print(f"Exhaurits els {MAX_INTENTS} intents. S'assigna prioritat 'mitjana'.")
# --- Classificacio del termini amb match ---
match prioritat:
case "alta":
termini = "Entrega avui mateix"
case "mitjana":
termini = "Entrega aquesta setmana"
case "baixa":
termini = "Entrega quan hi hagi lloc"
case _:
termini = "Termini per determinar"
print("=" * AMPLE)
print(f"{'Titol':<16}{titol:>{AMPLE - 16}}")
print(f"{'Responsable':<16}{f'{responsable} ({prioritat})':>{AMPLE - 16}}")
print(f"{'Termini':<16}{termini:>{AMPLE - 16}}")
print("=" * AMPLE)Aquí hi ha la peça clau de l'apartat 3, ara amb tot el sentit: el while té un else que només s'executa si s'han exhaurit els tres intents. Si la Marta l'encerta al segon, el break talla el bucle i l'else no arriba a executar-se, de manera que la prioritat conserva el valor que ella ha escrit. Si falla tres vegades, la condició intents < MAX_INTENTS es torna falsa per si sola, el bucle acaba de manera natural i l'else aplica el valor per defecte avisant-ne.
Prioritat (alta/mitjana/baixa) [1/3]: urgentissima 'urgentissima' no es valida. Prioritat (alta/mitjana/baixa) [2/3]: molt alta 'molt alta' no es valida. Prioritat (alta/mitjana/baixa) [3/3]: altissima 'altissima' no es valida. Exhaurits els 3 intents. S'assigna prioritat 'mitjana'. ============================================== Responsable Nuria (mitjana) Termini Entrega aquesta setmana ==============================================
Compara les tres versions del mateix problema i veuràs l'evolució del criteri: la 0.5.1 corregia la dada en silenci, la 0.5.2 exigia sense límit i la 0.5.3 negocia: insisteix un nombre raonable de vegades i, si no hi ha manera, segueix endavant dient exactament què ha decidit i per què.
Errors Comuns i Consells
Creure que l'else d'un bucle s'executa quan el bucle no fa voltes. No: s'executa quan el bucle no es trenca amb break. Un for de zero voltes executa el seu else, perquè ha acabat de manera natural.
Esperar que un break surti de dos bucles. Només trenca el bucle que el conté directament. Per sortir d'una imbricació calen una bandera i un break per nivell.
Col·locar continue abans de l'actualització en un while. És el bucle infinit més traïdor: continue salta a la condició sense haver incrementat el comptador. En un for no passa, perquè de la variable de control se n'encarrega Python.
Col·locar malament els case. Guanya el primer que coincideix, així que un case _: situat a dalt deixa inabastables tots els altres. I case dies > 15: no és un patró vàlid: els intervals són territori d'if / elif.
Confondre el nivell d'indentació en bucles imbricats. Una instrucció indentada un nivell de més s'executa N vegades en comptes d'una. És l'error que més resultats estranys produeix, i no dona cap avís.
Consell: anomena les banderes com a preguntes. cancellat, trobada, hi_ha_urgent. Així if cancellat: es llegeix com una frase i el codi s'explica sol.
Consell: posa un comptador de seguretat als while True, encara que creguis que la sortida està garantida. Un while True sense break assolible és un programa penjat, i en producció això és un incident.
Exercicis
Exercici 1: Predir el resultat
Sense executar res, indica què imprimeix cada fragment.
# a)
for i in range(5):
if i == 3:
break
print(i, end=" ")
else:
print("complet")
# b)
for i in range(5):
if i % 2 == 0:
continue
print(i, end=" ")
else:
print("complet")
# c)
prioritat = "urgent"
match prioritat:
case "alta" | "urgent":
print("avui")
case _:
print("un altre dia")Exercici 2: Cercador de tasques de la Nuria
Escriu cercar_nuria.py que pregunti quantes tasques hi ha al lot i després demani, una a una, el responsable de cada tasca. Tan bon punt trobi la primera de la Nuria ha d'imprimir en quina posició és i deixar de preguntar. Si les recorre totes sense trobar-la, ha d'avisar que la Nuria no té tasques assignades, fent servir la clàusula else del for. Descarta amb continue les entrades buides, sense que comptin com a resposta vàlida.
Exercici 3: Graella setmanal amb cancel·lació
Escriu graella_setmana.py que recorri amb bucles imbricats els 5 dies laborables i les 3 persones d'EQUIP, demanant les hores previstes de cada persona cada dia. Ha d'acumular el total d'hores de la setmana, permetre cancel·lar en qualsevol moment teclejant x (sortint dels dos bucles amb una bandera) i, en acabar, classificar la càrrega total amb match sobre un valor calculat: "lleugera" per sota de 80 hores, "normal" fins a 110 i "excessiva" per damunt. Pista: calcula primer la categoria amb if/elif i fes servir match per al missatge.
Solucions
Solució 1.
| Sortida | Per què | |
|---|---|---|
| a | 0 1 2 |
El break a i == 3 talla el bucle, així que l'else no s'executa |
| b | 1 3 complet |
continue salta els parells; el bucle acaba sense break, per tant l'else sí que s'executa |
| c | avui |
El patró "alta" | "urgent" accepta les dues paraules |
El contrast entre a i b és la lliçó sencera de l'else de bucle: el que decideix la seva execució no és quantes voltes s'han fet, sinó si hi ha hagut break.
Solució 2.
# cercar_nuria.py - Estudi Alba
CERCAT = "Nuria"
quantitat_tasques = int(input("Quantes tasques hi ha al lot? "))
for i in range(quantitat_tasques):
responsable = input(f"Responsable de la tasca {i + 1}: ").strip().capitalize()
if responsable == "":
print(" Entrada buida, s'ignora.")
continue
if responsable == CERCAT:
print(f"Primera tasca de {CERCAT}: la numero {i + 1}.")
break
else:
print(f"{CERCAT} no te cap tasca assignada en aquest lot.")Compte amb un matís de l'enunciat: el continue ignora l'entrada buida, però la volta es consumeix igual, perquè el for avança per la seva seqüència passi el que passi. Si volguessis que una entrada buida no gastés torn, necessitaries un while amb el teu propi comptador, que només incrementaries després d'una resposta vàlida.
Solució 3.
# graella_setmana.py - Estudi Alba
EQUIP = ("Marta", "Luis", "Nuria")
total_hores = 0
cancellat = False
for dia in range(1, 6):
for posicio in range(3):
persona = EQUIP[posicio]
entrada = input(f"Dia {dia} - hores de {persona} (x per sortir): ").strip().lower()
if entrada == "x":
cancellat = True
break
if entrada.isdigit():
total_hores += int(entrada)
if cancellat:
break
if total_hores < 80:
categoria = "lleugera"
elif total_hores <= 110:
categoria = "normal"
else:
categoria = "excessiva"
match categoria:
case "lleugera":
print(f"{total_hores} h: setmana lleugera, hi cap mes feina.")
case "normal":
print(f"{total_hores} h: setmana normal, planificacio sana.")
case "excessiva":
print(f"{total_hores} h: setmana excessiva, cal repartir.")Fixa't en el repartiment de papers, que és el missatge de fons d'aquesta lliçó: l'if / elif s'ocupa dels intervals numèrics, perquè match no els sap comparar; i match s'ocupa de traduir la categoria ja calculada en un missatge, que és exactament comparar un valor contra tres possibilitats. Cada eina en el seu terreny.
Conclusió
Ja controles el flux amb precisió. Saps tallar un bucle amb break —inclòs el patró while True amb sortida controlada— i descartar una volta amb continue, que és la clàusula de guarda dels bucles. Entens la clàusula else de for i while, que no significa «si no hi ha hagut voltes» sinó «si no hi ha hagut break», i coneixes el seu cas canònic: la cerca que acaba sense trobar res.
Saps imbricar bucles per recórrer graelles de dues dimensions —dies per persona—, tens clar que la feina total és el producte de tots dos (amb l'avís de cost que reprendrà la lliçó d'eficiència), i saps sortir d'una imbricació amb una bandera i un break per nivell, senyalitzant el final amb un sentinella anunciat a l'usuari. I manegues match / case amb els seus patrons | i el seu case _, sabent que serveix per comparar un valor contra alternatives concretes, mai per a intervals.
TascaFàcil és a la versió 0.5.3 i ha après a negociar: dona tres intents, avisa a cada errada, i si s'exhaureixen aplica un valor per defecte dient-ho en veu alta. La clàusula else del while distingeix amb exactitud «ho ha encertat» de «s'ha rendit», i un match tradueix la prioritat en un termini de lliurament.
I tanmateix, el programa continua sent un guió d'un sol acte: s'executa de dalt a baix, registra una tasca i mor. Si la Marta vol consultar la fitxa que acaba de crear, o canviar la prioritat que ha assignat malament, no li queda altre remei que tornar-lo a llançar i teclejar-ho tot de nou. Li falta el que converteix un script en una aplicació: un menú que es mostra, espera una ordre, l'executa i es torna a mostrar. Ja tens totes les peces —while True, break, validació, if/elif i match— i a Menús interactius i bucles d'aplicació les muntarem juntes per tancar el mòdul.
Fonaments de la Programació
Mòdul 1: Introducció a la Programació
- Què és la programació?
- Història de la programació
- Llenguatges de programació
- Entorns de desenvolupament
- Del problema a l'algorisme
Mòdul 2: Conceptes Bàsics
- Variables i tipus de dades
- Operadors i expressions
- Entrada i sortida de dades
- Conversió de tipus i validació de dades
Mòdul 3: Estructures de Control
Mòdul 4: Funcions i Procediments
- Definició i ús de funcions
- Paràmetres i retorn de valors
- Àmbit de variables
- Descompondre un programa en funcions
- Funcions com a valors: lambda i ordre superior
Mòdul 5: Estructures de Dades
- Llistes i arrays
- Cadenes de caràcters
- Diccionaris i conjunts
- Tuples i estructures imbricades
- Desar dades en fitxers: text, CSV i JSON
Mòdul 6: Algorismes Bàsics
Mòdul 7: Objectes i Organització del Codi
- De les dades als objectes: classes i instàncies
- Atributs, mètodes i constructor
- Col·leccions d'objectes
- Mòduls, paquets i importacions
Mòdul 8: Bones Pràctiques i Eines
- Documentació i comentaris
- Depuració i gestió d'errors
- Control de versions
- Proves automatitzades
- Estil, llegibilitat i refactorització
