Dipende quante volte
Come farsi piacere i broccoli
Milan Kundera scrive che il tempo umano non ruota in cerchio ma avanza veloce in linea retta, e che per questo l'uomo non può essere felice: la felicità è desiderio di ripetizione, e la linea retta non ripete nulla. È un'idea che ha la forza di un teorema e la malinconia di un addio. Ma chi passa le giornate a scrivere cicli sa che la faccenda è più complicata. Un ciclo non è mai un vero ritorno al punto di partenza. E proprio in questa differenza, tra il cerchio che si chiude su se stesso e il cerchio che avanza di un passo a ogni giro, si nasconde una storia da raccontare.Il for e la promessa di sapere già
Il ciclo for è prevalentemente enumerativo. Andrebbe usato solo quando si conosce a priori quante volte occorre eseguire una certa operazione: dieci righe di una matrice, i caratteri di una stringa, i giorni di un mese. È un ciclo di chi ha già fatto i conti prima di cominciare.
C'è però una tendenza ricorrente, in aula, a risolvere ogni problema con il for, anche quando il numero delle iterazioni non è affatto noto in anticipo. Per questo motivo, in una prima fase, faccio scrivere agli studenti con il while anche i cicli che sarebbero enumerativi: solo dopo mostro che il for è una scrittura compatta dello stesso ciclo, in cui inizializzazione, confronto e incremento vengono raccolti in un'unica riga invece di restare tre istruzioni separate.
La sintassi elegante nasconde il lavoro, non lo elimina.
Fuori dal codice, il for è la vita pianificata: il piano quinquennale, la dieta di trenta giorni, il countdown. Funziona quando il numero delle ripetizioni è davvero prevedibile. Il rischio è applicarlo per abitudine anche dove non si sa quante volte servirà, solo perché la forma compatta rassicura.
While, do-while e i broccoli
Quante volte viene eseguito un while? La risposta onesta è: dipende. Se la condizione è già falsa al primo controllo, il corpo del ciclo non viene eseguito nemmeno una volta. Il do-while, al contrario, garantisce almeno un'esecuzione: il controllo avviene dopo, non prima.
È la stessa differenza che separa chi si tira indietro perché intuisce subito che una cosa non porta da nessuna parte, e chi invece si impegna almeno una volta prima di giudicare. È l'argomento che si usa con il bambino che non vuole assaggiare i broccoli: prova almeno una volta, e solo allora potrai dire con cognizione di causa che non ti piacciono. Un genitore, in quel momento, sta insegnando un do-while: l'esperienza viene prima della condizione di uscita, non dopo.
Jarabe de Palo aveva già scritto il ritornello giusto per questa incertezza: depende. Dipende dalla condizione, dal contesto, da quello che si scopre solo entrando nel ciclo. Non è pigrizia dichiarare che la risposta dipende: è la constatazione onesta che alcuni cicli si possono valutare solo mentre li si attraversa.
Break, continue e i goto travestiti
Si insegna, giustamente, che il goto va evitato: rompe la leggibilità del programma e la possibilità di dimostrarne la correttezza, come segnalava già Dijkstra decenni fa. Böhm e Jacopini avevano peraltro già dimostrato, due anni prima, che tre sole strutture, sequenza, selezione e iterazione, bastano a scrivere qualunque algoritmo: il goto resta comodo, ma smette di essere necessario. Eppure break e continue sono goto con un nome più rispettabile, ammessi perché disciplinati e circoscritti a un singolo ciclo.
Un break dentro un ciclo annidato, usato per uscire quando si verifica una condizione imprevista, è la versione controllata del fermarsi in corsa: non previsto dal piano iniziale, ma un'uscita di sicurezza piuttosto che un cedimento. Un buon programma, come una buona vita, ha bisogno di qualche ancora di salvezza anche quando la struttura è pensata per essere lineare e prevedibile.
Il continue è un gesto diverso e più trascurato: non esce dal ciclo, salta il resto dell'iterazione corrente e passa alla successiva. È il "rifacciamo" detto a metà giro. E i motivi per dirlo sono opposti, anche se il comando è identico: si può ricominciare perché quel giro è andato male e non c'è ragione di portarlo a termine così com'è, oppure perché è andato così bene che si vuole subito riprovare, con la stessa condizione, per vedere se si ripete. Il codice non distingue le due intenzioni, la persona sì.
C'è poi un tabù ancora meno visibile, che i principianti quasi mai conoscono: non toccare la variabile di controllo del for dentro il corpo del ciclo. Modificarla a mano mentre il ciclo la sta già gestendo significa rompere la promessa fatta all'inizio, quella di sapere già quante volte si itererà. L'altra regola, ancora meno intuitiva, è non fare affidamento sul valore di quella variabile dopo che il ciclo è terminato: quel valore appartiene al ciclo, non a quello che viene dopo.
Si può scrivere codice da cinquant'anni e avere ancora il dubbio se si sia iterato una volta in più o una in meno. Ed è un bene che sia così: quel dubbio costringe a rileggere il proprio codice invece di fidarsi ciecamente della prima stesura.
C'è un caso in cui la domanda quante volte non si pone nemmeno: il foreach. Non conta, attraversa: prende un elemento alla volta finché la collezione ha elementi da offrire, senza indice da inizializzare, confrontare, incrementare a mano. Chi scrive un foreach non promette di sapere in anticipo quante volte, promette solo di considerare ciascun elemento una volta, né una di più né una di meno, delegando interamente alla collezione la domanda che il for si porta dietro fin dall'inizio.
Anche qui, però, c'è un tabù speculare a quello della variabile di controllo: non modificare la collezione mentre la si sta attraversando. Aggiungere o togliere un elemento durante un foreach non lascia solo un dubbio, in molti linguaggi produce un errore esplicito, perché il ciclo si accorge che il terreno sotto i piedi è cambiato mentre ci stava ancora camminando sopra.
Il confessionale come debugger morale
Da bambini, in confessione, arrivavano sempre le stesse due domande: da solo o con altri, e soprattutto quante volte. Viste con gli occhi di oggi sono, letteralmente, le variabili di stato di un ciclo: un booleano che registra la compagnia, un contatore che registra la frequenza.
Che la recidiva determini la gravità è un'ipotesi plausibile solo dentro un approccio giuridico e tribunalizio alla morale, dove la colpa si misura per accumulo, quasi fosse un'aggravante da codice penale piuttosto che un giudizio sull'atto in sé. Ed è curioso che proprio la penitenza assegnata, sette Ave Maria, trasformasse quell'approccio in un ciclo vero e proprio: noi ragazzini a contare sulle dita da zero a sei, un for eseguito con l'unico obiettivo dichiarato di arrivare in fondo e tornare a giocare a pallone. Nessun valore cambiava a ogni iterazione, nessuna crescita si accumulava lungo il percorso: solo un contatore da azzerare prima che la libertà venisse restituita.
Quante volte devono volare le cannonate
Bob Dylan pone la stessa domanda su scala collettiva, in Blowin' in the Wind: chiede quante volte ancora le cannonate dovranno volare prima di essere bandite per sempre. Ogni strofa ripete la struttura e la applica a un caso diverso, le strade, i mari, le vite umane, e l'accumulo delle iterazioni è ciò che costruisce l'urgenza morale della canzone.
È un ciclo che non ha condizione di uscita dichiarata all'inizio, esattamente come il while: si continua a interrogare finché la risposta non cambia, senza sapere in anticipo dopo quante strofe arriverà. E infatti la canzone non risponde mai con un numero. Il ritornello finale rimanda la risposta al vento stesso, come a dire che il contatore non si azzera con una cifra ma con un cambiamento reale nelle condizioni del mondo. È lo stesso scarto che separa la penitenza contata sulle dita dal ciclo che accumula senso: un conto che si esaurisce da solo quando smette di avere senso, non quando arriva a un numero prestabilito. Applicato alla coscienza di una generazione, il contatore di Dylan misura quante volte una cosa ingiusta può ancora accadere prima di diventare intollerabile.
Che differenza fa se conto a partire da zero
In quasi ogni linguaggio di programmazione gli array partono da zero: il primo elemento occupa la posizione zero, non la posizione uno. È una convenzione arbitraria, difesa da Dijkstra con un argomento tutt'altro che arbitrario sugli intervalli semiaperti, ma che cambia comunque la relazione tra il nome di una cosa e la sua posizione.
Contare da zero significa trattare il primo passo come condizione di partenza, non ancora come passo compiuto. Contare da uno significa che ogni passo, fin dal primo, viene già registrato come conquista. Non è solo una questione di sintassi: è una scelta su dove far cominciare il merito.
Mettere in circolo
Metti in circolo il tuo amore, cantava Ligabue, per dire a chi rischiava di perdersi in una dipendenza che l'alternativa era far uscire qualcosa invece di continuare a chiuderlo dentro di sé. Circolo è la stessa radice di ciclo, eppure l'espressione non descrive un girare su se stessi: descrive il contrario, un cerchio che si apre per lasciar passare qualcosa verso l'esterno.
Il ciclo mestruale che si interrompe in una gravidanza è un esempio possibile di questa struttura, non il modello a cui ricondurre ogni corpo che lo attraversa: un caso in cui l'interruzione genera qualcosa che il solo ripetersi del ciclo non avrebbe mai potuto produrre da solo. Lo stesso schema vale per un risparmio che a un certo punto viene speso, per una competenza accumulata anno dopo anno che a un certo punto viene insegnata, per un affetto che resta vero solo quando esce dalla propria cerchia e arriva a qualcun altro. Nessuno di questi esempi dice cosa un ciclo debba fare, né tantomeno cosa debba fare chi lo vive. Dicono soltanto che, a volte, il giro più riuscito è quello che si apre verso l'esterno.
Circoli viziosi e virtuosi
Ripetere è monotono, purché ad ogni ciclo ci sia almeno un valore che cambia. È la definizione tecnica di iterazione, e insieme un criterio per distinguere due tipi di cerchio che a prima vista sembrano identici. Il circolo vizioso è un'iterazione in cui lo stato peggiora o resta uguale a se stesso a ogni giro, come il loop infinito di un programma bloccato: la condizione non cambia mai, e il ciclo continua a girare a vuoto. Il circolo virtuoso è quello in cui ogni passaggio lascia qualcosa in più rispetto al precedente.
La differenza non sta nella forma del ciclo, che può essere identica in entrambi i casi. Sta nel segno di quello che cambia a ogni iterazione. Forse Kundera aveva ragione a dire che la felicità è desiderio di ripetizione, ma la ripetizione che rende felici non è mai quella che torna sempre uguale a se stessa. È quella che, girando, accumula qualcosa: un'esperienza in più, come i broccoli assaggiati almeno una volta; una consapevolezza in più, come le cannonate contate da Dylan; un passo in più, anche quando si continua a contare da zero. E a volte, come nel gesto di mettere in circolo ciò che un ciclo ha accumulato, il giro più riuscito è quello che si apre verso l'esterno, lasciando entrare qualcosa che, dentro il ciclo, mancava.
Riferimenti
- Milan Kundera, L'insostenibile leggerezza dell'essere, Adelphi, Milano 1985 (ed. orig. Nesnesitelná lehkost bytí, 1984).
- Bob Dylan, Blowin' in the Wind, in The Freewheelin' Bob Dylan, Columbia Records, 1963.
- Luciano Ligabue, Metti in circolo il tuo amore, in Radiofreccia, WEA/Warner Music Italia, 1998.
- Jarabe de Palo, Depende, in Depende, EMI, 1998.
- Corrado Böhm, Giuseppe Jacopini, Flow Diagrams, Turing Machines and Languages with Only Two Formation Rules, in «Communications of the ACM», vol. 9, n. 5, 1966, pp. 366-371.
- Edsger W. Dijkstra, Go To Statement Considered Harmful, in «Communications of the ACM», vol. 11, n. 3, 1968, pp. 147-148.
- Edsger W. Dijkstra, Why Numbering Should Start at Zero, EWD831, circolare privata, Università del Texas ad Austin, 1982.

Commenti