singe/docs/it/lessons/05-functions.adoc

709 lines
24 KiB
Text

[#_lesson_5_your_own_functions]
== Lezione 5: Funzioni tutte tue
image::learn/05-functions.png[La lezione finita, 480]
La quarta lezione è finita con un avvertimento sul tuo stesso programma:
`onOverlayUpdate` calcola dove vanno centoventi stelle, decide quanto è luminosa
ognuna, le disegna e conta la discesa, ed è una sola funzione che fa quattro
lavori. Le stanno per chiedere di farne un quinto, perché un campo di stelle
senza niente davanti non è un gioco.
In questa lezione la smonti. Scriverai funzioni tutte tue -- funzioni di cui
Singe non ha mai sentito parlare e che non chiamerà mai -- e passerai a loro il
lavoro. Alla fine tutto il campo di stelle è una riga che dice `drawStars()`, in
fondo allo schermo è tornata un'astronave, e il programma è più facile da
leggere di quando faceva meno.
[#_start_a_new_folder_4]
=== Crea una nuova cartella
Una cartella chiamata `functions`, dentro un file chiamato `functions.singe`, e
`Singe -R functions` in esecuzione in un terminale.
Copiaci dentro il programma finito della quarta lezione. Eccolo, invariato, e
vale la pena eseguirlo una volta prima di toccare qualsiasi cosa:
[source,lua]
----
LAYERS = 3
STARS_PER_LAYER = 40
ACROSS_STEP = 37
DOWN_STEP = 61
LAYER_SHIFT = 13
LAYER_SPEED = 0.5
LAYER_BRIGHT = 55
width = overlayGetWidth()
height = overlayGetHeight()
drift = 0
function onOverlayUpdate()
drift = drift + 1
overlayClear()
for layer = 1, LAYERS do
for star = 1, STARS_PER_LAYER do
starX = (star * ACROSS_STEP + layer * LAYER_SHIFT) % width
starY = (star * DOWN_STEP + math.floor(drift * layer * LAYER_SPEED)) % height
shade = math.random(60, 90) + layer * LAYER_BRIGHT
colorForeground(shade, shade, shade)
overlayPlot(starX, starY)
end
end
return OVERLAY_UPDATED
end
----
Tre strati di stelle che scendono lungo la finestra a tre velocità. Niente nella
prima metà di questa lezione cambia quello che vedi. Tutto quello che c'è cambia
il modo in cui si legge il programma.
[#_one_job_one_name]
=== Un lavoro, un nome
Comincia dalle due righe che mettono una stella sullo schermo. Sopra
`onOverlayUpdate`, aggiungi questo:
[source,lua]
----
function drawStar(x, y, shade)
colorForeground(shade, shade, shade)
overlayPlot(x, y)
end
----
Poi, nel ciclo interno, cancella quelle due righe e al loro posto chiama la
funzione:
[source,lua]
----
for star = 1, STARS_PER_LAYER do
starX = (star * ACROSS_STEP + layer * LAYER_SHIFT) % width
starY = (star * DOWN_STEP + math.floor(drift * layer * LAYER_SPEED)) % height
shade = math.random(60, 90) + layer * LAYER_BRIGHT
drawStar(starX, starY, shade)
end
----
Salva. L'immagine è la stessa.
Hai già scritto delle funzioni -- `onOverlayUpdate` è una di queste, e lo sono
anche `onInputPressed` e `onInputReleased` della terza lezione -- ma quelle sono
callback. Tu le scrivi e Singe le chiama. `drawStar` è diversa. Singe non ne ha
mai sentito parlare. Viene eseguita perché *tu* hai scritto il suo nome e le
parentesi, nello stesso modo in cui chiami `overlayPlot`, e niente al mondo la
eseguirà se non lo fai tu.
Il trucco è tutto qui. D'ora in poi, quando ti trovi a pensare «e adesso
disegna una stella», puoi scrivere «disegna una stella» e intenderlo davvero.
[#_a_parameter_is_a_name_not_a_thing]
=== Un parametro è un nome, non una cosa
Guarda di nuovo la prima riga di `drawStar`.
[source,lua]
----
function drawStar(x, y, shade)
----
I tre nomi tra le parentesi sono *parametri*. Un parametro è un nome che la
funzione usa per qualunque cosa le abbia passato chi la chiama. Quando scrivi
`drawStar(starX, starY, shade)`, Lua mette in `x` quello che c'è in `starX`, in
`y` quello che c'è in `starY`, in `shade` quello che c'è in `shade`, e poi
esegue la funzione.
I nomi non hanno niente a che fare tra loro. Il numero di chi chiama là fuori si
chiama `starX` e qui dentro `x`, e funzionerebbe esattamente allo stesso modo se
lo chiamassi `across` o `banana`. Dentro la funzione esistono solo i nomi tra le
parentesi, e contengono copie di quello che è entrato.
È qui che le parentesi vuote della prima lezione finalmente hanno senso.
[source,lua]
----
function onOverlayUpdate()
----
Niente tra le parentesi vuol dire nessun parametro: il motore non ha niente da
dire a questa funzione, quindi non c'è niente a cui dare un nome. Confrontala
con la callback che hai scritto nella terza lezione.
[source,lua]
----
function onInputPressed(what)
----
Un parametro, chiamato `what`. A questa il motore ha qualcosa da dire -- quale
comando è stato premuto -- quindi c'è un nome in cui riceverlo. Usi un parametro
dalla terza lezione.
Un parametro è davvero la copia personale della funzione, e vale la pena
dimostrartelo una volta. Scrivi questa, chiamala da `onOverlayUpdate` e poi
toglila di nuovo:
[source,lua]
----
function half(number)
number = number // 2
return number
end
----
Chiamala come `half(drift)`. La funzione dimezza il suo `number` e restituisce
la risposta, e `drift`, là fuori in `onOverlayUpdate`, continua a contare come
se niente fosse. Una funzione non può allungare la mano e cambiare le variabili
di chi la chiama attraverso un parametro. Riceve una copia, e quello che fa alla
copia sono affari suoi.
[#_handing_an_answer_back]
=== Restituire una risposta
`half` ha fatto una cosa che le funzioni scritte da te finora non facevano: ha
dato una risposta. È l'ultima riga.
[source,lua]
----
return number
----
`return` restituisce un valore a chi ha chiamato la funzione, e la chiamata si
trasforma in quel valore. Scrivi `smaller = half(drift)` e Lua esegue `half`,
prende quello che `half` ha restituito e lo mette in `smaller`. Hai incontrato
`return` nella prima lezione, dove `onOverlayUpdate` restituisce
`OVERLAY_UPDATED` per rispondere alla domanda di Singe se fosse cambiato
qualcosa. Qui è la stessa parola che fa lo stesso lavoro, solo che adesso la
domanda la fai tu.
La riga della luminosità implora di diventare una funzione con una risposta.
Aggiungi questa:
[source,lua]
----
function shadeFor(layer)
return math.random(60, 90) + layer * LAYER_BRIGHT
end
----
Tutto il corpo è un `return`, cosa normale per una funzione piccola. Ora il
ciclo interno può passare la risposta direttamente a `drawStar`, senza un nome
in mezzo:
[source,lua]
----
drawStar(starX, starY, shadeFor(layer))
----
Una chiamata dentro una chiamata. Lua calcola prima quella interna, riceve un
numero da `shadeFor` e passa quel numero a `drawStar` come terzo argomento. Hai
già scritto qualcosa di simile senza accorgertene: il
`math.floor(drift * layer * LAYER_SPEED)` della quarta lezione è una chiamata la
cui risposta viene usata sul momento, in mezzo a un calcolo più grande.
[#_two_answers_at_once]
=== Due risposte insieme
Le due righe della posizione sono più difficili, perché sono due numeri
calcolati insieme e dovresti sceglierne uno da restituire. Nella maggior parte
dei linguaggi una funzione può restituire un solo valore, e tirarne fuori due è
una seccatura. Lua ti lascia restituirne quanti ne vuoi.
[source,lua]
----
function starAt(star, layer)
local x = (star * ACROSS_STEP + layer * LAYER_SHIFT) % width
local y = (star * DOWN_STEP + math.floor(drift * layer * LAYER_SPEED)) % height
return x, y
end
----
Raccogli le risposte mettendo due nomi a sinistra, separati da una virgola:
[source,lua]
----
local x, y = starAt(star, layer)
----
Il primo valore nel primo nome, il secondo nel secondo. Se chiedi meno nomi di
quanti valori restituisce la funzione, i valori in più vengono buttati via:
`local x = starAt(star, layer)` ti dà il numero orizzontale e perde quello
verticale, senza lamentarsi. Se chiedi più nomi di quanti sono i valori, quelli
in più sono `nil`.
Ignora la parola `local` in quei due blocchi ancora per un minuto. Sta arrivando
una sezione tutta sua, ed è quella importante.
[#_the_star_field_in_one_line]
=== Il campo di stelle in una riga
Il ciclo interno ora è tre righe brevi, tutte su uno strato di stelle. Questo è
un lavoro con un nome.
[source,lua]
----
function drawLayer(layer)
for star = 1, STARS_PER_LAYER do
local x, y = starAt(star, layer)
drawStar(x, y, shadeFor(layer))
end
end
----
E anche il ciclo esterno è un lavoro con un nome:
[source,lua]
----
function drawStars()
for layer = 1, LAYERS do
drawLayer(layer)
end
end
----
Funzioni che chiamano funzioni che chiamano funzioni, tre livelli di
profondità, e ognuna è tre o quattro righe che riesci a tenere in testa tutte
insieme. Ora `onOverlayUpdate` può dire che cosa *è* un fotogramma:
[source,lua]
----
function onOverlayUpdate()
drift = drift + 1
overlayClear()
drawStars()
return OVERLAY_UPDATED
end
----
Ecco la promessa fatta alla fine della quarta lezione, mantenuta.
[#_put_the_ship_back]
=== Rimetti l'astronave
Ora il quinto lavoro, quello che avrebbe fatto un disastro della vecchia
`onOverlayUpdate`. Riporta il riquadro che guidavi nella terza lezione, stavolta
come un'astronave ferma vicino al fondo dello schermo che si muove solo a destra
e a sinistra.
Le servono alcuni valori suoi, su in cima insieme agli altri, e le due callback
dei comandi della terza lezione senza i rami per su e giù:
[source,lua]
----
PLAYER_WIDTH = 20
PLAYER_HEIGHT = 6
PLAYER_SPEED = 3
PLAYER_MARGIN = 6
playerX = width // 2
playerY = height - PLAYER_HEIGHT - PLAYER_MARGIN
movingLeft = false
movingRight = false
----
[source,lua]
----
function onInputPressed(what)
if what == SWITCH_LEFT then
movingLeft = true
elseif what == SWITCH_RIGHT then
movingRight = true
end
end
function onInputReleased(what)
if what == SWITCH_LEFT then
movingLeft = false
elseif what == SWITCH_RIGHT then
movingRight = false
end
end
----
Disegnarla sono due righe, quindi è una funzione:
[source,lua]
----
function drawPlayer()
colorForeground(80, 255, 120)
overlayBox(playerX, playerY, playerX + PLAYER_WIDTH, playerY + PLAYER_HEIGHT)
end
----
Muoverla è il punto in cui la lezione dà i suoi frutti. Nella terza lezione,
tenere il riquadro sullo schermo richiedeva quattro righe di `if` ed `elseif`
per ogni direzione. Quell'idea -- tenere un numero tra un minimo e un massimo --
ha un nome, e il nome è *clamp*:
[source,lua]
----
function clamp(value, low, high)
if value < low then
return low
end
if value > high then
return high
end
return value
end
----
Tre return, e al massimo uno di loro avviene, perché `return` non si limita a
restituire una risposta: ferma la funzione di colpo. Niente di quello che viene
dopo viene eseguito. Se il valore è sotto il minimo, restituisci il minimo e
fermati. Se è sopra il massimo, restituisci il massimo e fermati. Altrimenti
restituisci quello che è entrato.
[source,lua]
----
function movePlayer()
if movingLeft then
playerX = playerX - PLAYER_SPEED
end
if movingRight then
playerX = playerX + PLAYER_SPEED
end
playerX = clamp(playerX, 0, width - PLAYER_WIDTH - 1)
end
----
Una funzione che finisce senza un `return` restituisce niente, cioè `nil`. Va
bene per `drawPlayer` e `movePlayer`, che vengono chiamate per fare qualcosa e
non per rispondere a qualcosa. È un bug in una funzione come `clamp`: togli
quell'ultimo `return value` e ogni posizione in mezzo all'intervallo torna
indietro come `nil`.
[#_where_names_live]
=== Dove vivono i nomi
Ogni nome che hai creato dalla seconda lezione in poi è stato una *variabile
globale*. Una globale appartiene a tutto il programma: `drift`, `playerX` e
`LAYERS` possono essere letti e scritti da qualsiasi funzione del file, in
qualsiasi momento, e restano lì finché il gioco gira.
Sembra comodo, e con sei nomi lo è. I guai cominciano a sessanta. Ogni globale è
un nome che tutto il programma ha accettato di riservare, quindi quando un'altra
funzione vuole un posto dove tenere una posizione orizzontale e lo chiama anche
lei `starX`, i due sono la stessa scatola, e ognuno scrive sopra il numero
dell'altro. Quando una globale contiene qualcosa di sbagliato, qualsiasi cosa
nel file potrebbe avercelo messo, e scoprire che cosa vuol dire leggerlo tutto.
La cura è una *locale*. Metti la parola `local` davanti a un nome la prima volta
che lo usi, e il nome esiste solo dentro la `function`, il ciclo o il blocco `if` in cui
è stato scritto.
[source,lua]
----
function starAt(star, layer)
local x = (star * ACROSS_STEP + layer * LAYER_SHIFT) % width
local y = (star * DOWN_STEP + math.floor(drift * layer * LAYER_SPEED)) % height
return x, y
end
----
Qui `x` e `y` non sono affari di nessun altro. Un'altra funzione può avere la
sua `x` e le due non si incontrano mai. Quando la `x` di `starAt` è sbagliata,
le tre righe qui sopra sono le uniche tre che possono averlo fatto. Senza la
parola `local`, quelle due righe creerebbero in silenzio altre due globali, che è
esattamente ciò che `starX` e `starY` sono sempre stati.
Ora la parte che sorprende tutti. Scrivi questa funzione, e metti in
`onOverlayUpdate` una riga che ti mostri che cosa restituisce:
[source,lua]
----
function countUp()
local counted = 0
counted = counted + 1
return counted
end
----
[source,lua]
----
overlayPrint(0, 0, "Count: " .. countUp())
----
`Count: 1`, e resta `Count: 1` finché il gioco gira, anche se quella funzione
viene chiamata sessanta volte al secondo e aggiunge uno ogni volta. Una locale
viene creata nuova quando la funzione comincia e sparisce quando la funzione
finisce, quindi `counted` nasce come `0`, diventa `1` e muore, sessanta volte al
secondo, per sempre. Una locale non ricorda. Quando l'hai visto, togli di nuovo
le due cose.
Non è un difetto, è proprio lo scopo: una locale non può essere avvelenata dalla
chiamata precedente, perché della chiamata precedente non resta niente. Quando
invece devi ricordare qualcosa tra una chiamata e l'altra -- `drift` e `playerX`
devono ricordare -- il nome deve vivere fuori da tutte le funzioni. Mettilo in
cima al file con `local` davanti:
[source,lua]
----
local drift = 0
local playerX = width // 2
----
Un `local` scritto in cima al file, fuori da tutte le funzioni, vive quanto lo
script e può essere visto da ogni funzione che sta sotto. Quell'ultima parola
conta: *sotto*. Una locale esiste dalla sua riga in giù, quindi una funzione
scritta sopra la riga che la crea non può vederla. Lo stesso vale per le
funzioni stesse una volta che le segni come `local`, ed è per questo che lo
script finito è nell'ordine in cui è: prima i valori, poi le tue funzioni, poi,
per ultime, le callback.
Adesso passa tutto il file e metti `local` davanti a ogni nome che hai creato --
le costanti in maiuscolo, `width` e `height`, `drift`, i quattro valori del
giocatore e ognuna delle tue funzioni. Tre nomi non lo ricevono:
`onInputPressed`, `onInputReleased` e `onOverlayUpdate` devono restare globali,
perché Singe le trova per nome e un nome locale è invisibile da fuori del file.
Sii onesto su quello che le locali non risolvono. Se sbagli a scrivere un nome a
cui stai assegnando un valore -- `movinLeft = true` invece di
`movingLeft = true` -- Lua non si lamenta. Crea una globale nuova di zecca con
il tuo errore di battitura come nome, la imposta e lascia stare quella vera. Non
si muove niente, non c'è nessun errore, e tu lo fissi per dieci minuti. Da
quello le locali non ti salvano. Leggere il nome due volte sì.
[#_the_whole_thing_4]
=== Tutto insieme
Ecco il programma finito, con dentro ogni pezzo della lezione.
[source,lua]
----
local LAYERS = 3
local STARS_PER_LAYER = 40
local ACROSS_STEP = 37
local DOWN_STEP = 61
local LAYER_SHIFT = 13
local LAYER_SPEED = 0.5
local LAYER_BRIGHT = 55
local PLAYER_WIDTH = 20
local PLAYER_HEIGHT = 6
local PLAYER_SPEED = 3
local PLAYER_MARGIN = 6
local width = overlayGetWidth()
local height = overlayGetHeight()
local drift = 0
local playerX = width // 2
local playerY = height - PLAYER_HEIGHT - PLAYER_MARGIN
local movingLeft = false
local movingRight = false
local function clamp(value, low, high)
if value < low then
return low
end
if value > high then
return high
end
return value
end
local function shadeFor(layer)
return math.random(60, 90) + layer * LAYER_BRIGHT
end
local function starAt(star, layer)
local x = (star * ACROSS_STEP + layer * LAYER_SHIFT) % width
local y = (star * DOWN_STEP + math.floor(drift * layer * LAYER_SPEED)) % height
return x, y
end
local function drawStar(x, y, shade)
colorForeground(shade, shade, shade)
overlayPlot(x, y)
end
local function drawLayer(layer)
for star = 1, STARS_PER_LAYER do
local x, y = starAt(star, layer)
drawStar(x, y, shadeFor(layer))
end
end
local function drawStars()
for layer = 1, LAYERS do
drawLayer(layer)
end
end
local function drawPlayer()
colorForeground(80, 255, 120)
overlayBox(playerX, playerY, playerX + PLAYER_WIDTH, playerY + PLAYER_HEIGHT)
end
local function movePlayer()
if movingLeft then
playerX = playerX - PLAYER_SPEED
end
if movingRight then
playerX = playerX + PLAYER_SPEED
end
playerX = clamp(playerX, 0, width - PLAYER_WIDTH - 1)
end
function onInputPressed(what)
if what == SWITCH_LEFT then
movingLeft = true
elseif what == SWITCH_RIGHT then
movingRight = true
end
end
function onInputReleased(what)
if what == SWITCH_LEFT then
movingLeft = false
elseif what == SWITCH_RIGHT then
movingRight = false
end
end
function onOverlayUpdate()
drift = drift + 1
overlayClear()
movePlayer()
drawStars()
drawPlayer()
return OVERLAY_UPDATED
end
----
Prima di andare avanti vale la pena indicare due cose.
`local function drawStar(x, y, shade)` segna come locale la funzione stessa. Le
tue funzioni sono nomi come tutti gli altri, e appartengono solo a questo file.
Le tre callback restano globali così Singe può trovarle.
E ora `onOverlayUpdate` è sei righe che dicono che cos'è un fotogramma: conta la
discesa, pulisci lo schermo, muovi il giocatore, disegna il cielo, disegna il
giocatore, fatto. Fa cinque lavori nominando cinque cose che fanno un lavoro
ciascuna, che è la forma di ogni ciclo di gioco che scriverai.
[#_a_function_is_a_name_for_an_idea]
=== Una funzione è un nome per un'idea
`clamp` viene chiamata una volta sola. Tirarla fuori non ha risparmiato nessuna
battitura -- quattro righe sono uscite, una è entrata, e da un'altra parte è
comparsa una funzione di nove righe. Valeva comunque la pena farlo.
Una funzione non è solo un modo per non ripeterti. È un modo per dare un nome a
un'idea, così il codice che usa l'idea può dire il nome e andare avanti.
«Tieni il giocatore tra il bordo sinistro e il bordo destro» è un pensiero che
hai avuto una volta. `clamp(playerX, 0, width - PLAYER_WIDTH - 1)` è quel
pensiero, messo per iscritto. Quattro istruzioni `if` sono lo stesso pensiero
smontato in pezzi che devi rimettere insieme in testa ogni volta che li leggi.
Quando stai decidendo se una cosa merita di essere una funzione, non chiederti
quante volte la chiamerai. Chiediti se ha un nome.
[#_try_it_5]
=== Prova tu
. *Rinomina un parametro.* In `drawStar`, cambia `x` e `y` in `across` e
`down`, ovunque dentro la funzione. Non toccare niente fuori. Eseguilo. Scopri
perché non si è rotto niente.
. *Disegnali al contrario.* Scambia `drawStars()` e `drawPlayer()` in
`onOverlayUpdate` e guarda l'astronave. Poi rimettili a posto. Ognuna di
quelle cinque righe è un lavoro, e il loro ordine sullo schermo è il loro
ordine nella funzione.
. *Usa `clamp` su una sfumatura.* Cambia `shadeFor` in
`return clamp(math.random(60, 90) + layer * 90, 0, 200)` e guarda i tre
strati. Poi togli il `clamp` e guarda di nuovo: `colorForeground` ti teneva in
silenzio i numeri sotto il limite da sempre, e ora puoi vedere dove.
. *Chiama `drawStar` con due argomenti.* Scrivi `drawStar(100, 100)` da qualche
parte in `drawLayer` ed eseguilo. L'errore nomina esattamente la cosa che è
mancata. Leggilo prima di rimettere il terzo argomento.
. *Sposta una locale.* Prendi la riga `local drift = 0` e spostala in fondo al
file, sotto tutto il resto. Eseguilo, leggi l'errore e pensa alla parola
«sotto» nella sezione precedente.
[#_break_it_on_purpose_5]
=== Rompilo apposta
L'ordine di un file con dentro delle locali conta, e l'errore che ottieni quando
rompi quell'ordine non è quello che ti aspetti.
Prendi tutta la funzione `drawStar` e spostala in *fondo* allo script, sotto
`onOverlayUpdate`. C'è ancora tutto, scritto allo stesso modo. Eseguilo:
----
Error executing function 'onOverlayUpdate': functions/functions.singe:50: attempt to call a nil value (global 'drawStar')
stack traceback:
functions/functions.singe:50: in upvalue 'drawLayer'
functions/functions.singe:57: in upvalue 'drawStars'
functions/functions.singe:101: in function 'onOverlayUpdate'
----
Ha un aspetto diverso dall'errore della prima lezione perché è un tipo di errore
diverso. L'errore della prima lezione è stato colto prima che lo script
partisse. Questo è stato colto mentre era in esecuzione, ed è per questo che
Singe nomina la funzione in cui si trovava, e per questo sotto c'è un
*traceback*: l'elenco di chi ha chiamato chi, dal più recente. La riga 50 è
dentro `drawLayer`, che è stata chiamata dalla riga 57 dentro `drawStars`, che è
stata chiamata dalla riga 101 dentro `onOverlayUpdate`. I tuoi numeri di riga
saranno diversi se il tuo file non corrisponde esattamente a quello qui sopra, e
il motore potrebbe aggiungere sotto queste una o due righe sue. La parola
`upvalue` è il nome che Lua dà a una locale che una funzione ha preso in
prestito dal file che la circonda; per ora passaci sopra.
La lamentela in sé vale la pena di impararla a memoria. «Attempt to call a nil
value» vuol dire che hai messo delle parentesi dopo un nome che non conteneva
niente. La parola `global` davanti a `drawStar` è la parte che svela tutto: hai segnato `drawStar`
come locale, quindi quando Lua ha letto `drawLayer` e non ha trovato da nessuna
parte sopra una locale con quel nome, l'unica cosa che restava da provare era
una globale -- e non c'è nemmeno una globale chiamata `drawStar`. Rimetti la
funzione sopra il codice che la chiama e l'errore se ne va con lei.
[#_what_you_learned_5]
=== Cosa hai imparato
* Puoi scrivere funzioni tutte tue, e niente le esegue tranne le tue chiamate.
* I nomi tra le parentesi di una funzione sono parametri, e sono le copie
personali della funzione di quello che ha passato chi la chiama.
* `return` restituisce un valore e ferma la funzione lì dov'è, e una funzione
che finisce senza di esso restituisce `nil`.
* Una chiamata può essere un argomento di un'altra chiamata, e quella interna
avviene per prima.
* Lua può restituire più di un valore, e li raccogli con più nomi separati da
virgole.
* `local` crea un nome che esiste solo dove è stato scritto.
* Una locale dentro una funzione viene creata nuova a ogni chiamata e non
ricorda niente di quella precedente.
* Una locale si vede solo sotto la riga che la crea, quindi i valori e le tue
funzioni vanno sopra le callback che li usano.
* Le callback restano globali, perché Singe le trova per nome.
* Vale la pena scrivere una funzione quando la cosa che fa ha un nome, anche se
la chiami una volta sola.
[#_next_time_5]
=== La prossima volta
Le tue centoventi stelle sono ancora centoventi copie di tre idee. Niente nel
programma è *una stella*: c'è un numero, e un po' di aritmetica che trasforma il
numero in una posizione. Non puoi dire «questa è più vicina, quindi cade più in
fretta e brilla di più», perché non c'è nessun «questa» di cui dirlo, e non puoi
lasciare che una stella cada fuori dal fondo e venga sostituita da una nuova,
perché lì non c'è niente da togliere.
La sesta lezione ti dà la cosa che contiene una stella, e poi un modo per
contenerne centoventi e aggiungerne una centoventunesima mentre il gioco gira. È
la lezione più utile di questa parte del libro.