Parte II — Il flusso
Capitolo 9 — Le funzioni
Man mano che un programma cresce, cominci a ripetere gli stessi gruppi di istruzioni. Le funzioni servono a dare un nome a un blocco di codice, così da poterlo richiamare quando serve invece di riscriverlo. Sono il mattone con cui si costruiscono i programmi grandi: ogni funzione fa una cosa, la fa bene, e il resto del programma la usa senza doversi preoccupare di come.
🦁 Curiosità — Una parola presa in prestito
Il termine «funzione» arriva dalla matematica, dove indica una regola che a ogni ingresso associa un'uscita: dai un numero, ne ottieni un altro. In programmazione l'idea è la stessa, con un vantaggio pratico enorme: scrivi una cosa una volta e la riusi cento. Questo principio ha persino un nome e una sigla, DRY, da «Don't Repeat Yourself» — non ripeterti — reso celebre dal libro The Pragmatic Programmer del 1999.
Definire e chiamare
Una funzione si definisce con funzione, seguita dal nome e, tra parentesi, dai parametri — i dati che riceve. Dentro, restituisci dice quale valore mandare indietro:
funzione saluta(nome, saluto = "Ciao")
restituisci "{saluto}, {nome}!"
fine
scrivi(saluta("Giulia"))
scrivi(saluta("Marco", saluto = "Buongiorno"))Ciao, Giulia!
Buongiorno, Marco!💡 L'idea — Come un distributore automatico
Una funzione è come un distributore automatico: inserisci qualcosa (gli argomenti), premi il pulsante e ne esce un risultato. Non hai bisogno di sapere come funziona dentro — ti fidi di quello che restituisce. È questo il bello delle funzioni: una volta scritte bene, le usi senza più pensare a come fanno il loro mestiere.
Guarda il parametro saluto = "Ciao": è un valore predefinito. Se chi chiama la funzione non lo fornisce (come nel primo caso), vale "Ciao"; se lo fornisce (secondo caso), vince il suo. E nota come, nella seconda chiamata, l'abbiamo passato per nome: saluto = "Buongiorno". Passare gli argomenti per nome rende le chiamate leggibili, specie quando i parametri sono tanti.
Una regola da ricordare: una funzione va definita prima di essere chiamata. Se la metti dopo il punto in cui la usi, spostala sopra — oppure usa il blocco inizio del Capitolo 1, che parte quando tutto il file è già stato letto.
Le funzioni come comandi
Hai notato nel Capitolo 1 che scrivi si usa senza parentesi? La stessa cortesia vale per le tue funzioni, a una condizione: che non abbiano restituisci, cioè che facciano qualcosa invece di calcolare un valore. In quel caso puoi chiamarle come comandi, con gli argomenti separati da virgole e senza parentesi:
funzione saluta(nome)
scrivi "Ciao, {nome}!"
fine
saluta "Ada Lovelace"Ciao, Ada Lovelace!Le funzioni che invece restituiscono un valore vogliono le parentesi, come in un foglio di calcolo: saluta("Giulia"), radice(16), media_di(voti...). La regola è coerente con tutto il resto: le parentesi dicono «calcola e dammi un valore».
Le funzioni sono valori
Ecco un'idea che all'inizio spiazza e poi diventa indispensabile: in Leo una funzione è un valore come un numero o un testo. Puoi metterla in una variabile, passarla a un'altra funzione, restituirla. Puoi anche creare una funzione senza nome — una funzione anonima — al volo:
variabile doppio = funzione (x) restituisci x * 2 fine
scrivi(doppio(21))
variabile voti = [4, 6, 7, 9, 10]
scrivi(mappa(voti, doppio))
scrivi(filtra(voti, funzione (v) restituisci v >= 6 fine))42
[8, 12, 14, 18, 20]
[6, 7, 9, 10]mappa(lista, funzione) applica una funzione a ogni elemento e restituisce la nuova lista; filtra(lista, funzione) tiene solo gli elementi per cui la funzione dà vero. A entrambe passiamo una funzione: nel primo caso doppio (per nome), nel secondo una funzione anonima scritta sul posto. Questo stile — passare comportamenti, non solo dati — è potentissimo, e lo ritroveremo parlando della libreria.
Le chiusure: funzioni che ricordano
Quando definisci una funzione dentro un'altra, quella interna ricorda le variabili del posto in cui è nata, anche dopo che la funzione esterna è terminata. Questo meccanismo si chiama chiusura, ed è più facile vederlo che spiegarlo:
funzione crea_contatore()
variabile conteggio = 0
funzione incrementa()
conteggio += 1
restituisci conteggio
fine
restituisci incrementa
fine
variabile conta = crea_contatore()
conta()
conta()
scrivi(conta())3crea_contatore crea una variabile conteggio e restituisce la funzione incrementa, che la usa. Anche se crea_contatore è già finita, la funzione conta continua ad avere accesso al suo conteggio, che cresce a ogni chiamata. Ogni contatore creato così ha un conteggio tutto suo, indipendente dagli altri. Le chiusure sono il modo elegante di «incapsulare» uno stato senza bisogno di variabili globali.
Quanti argomenti vuoi
A volte non sai in anticipo quanti argomenti riceverà una funzione. Tre puntini dopo l'ultimo parametro li raccolgono tutti in una lista; e tre puntini nella chiamata fanno il contrario, srotolando una lista in tanti argomenti:
funzione media_di(valori...)
restituisci somma(valori) / lunghezza(valori)
fine
scrivi(media_di(6, 7, 8))
variabile voti = [5, 7, 9]
scrivi(media_di(voti...))7.0
7.0La stessa funzione accetta sia argomenti sciolti (media_di(6, 7, 8)) sia una lista già pronta srotolata con i puntini (media_di(voti...)).
La ricorsione
Una funzione può chiamare sé stessa. Sembra un gioco di prestigio, ma è il modo naturale per risolvere i problemi che si definiscono in termini di sé stessi — come il fattoriale, dove 5! è 5 x 4!:
funzione fattoriale(n)
se n <= 1 allora
restituisci 1
fine
restituisci n * fattoriale(n - 1)
fine
scrivi(fattoriale(5))120La chiave di ogni ricorsione è il caso base — qui se n <= 1, che ferma la discesa. Senza di esso, la funzione si richiamerebbe all'infinito; Leo ti protegge fermandosi dopo 1000 chiamate annidate con un ErroreRicorsione, ma il caso base devi sempre scriverlo tu.
Prova tu
- Scrivi una funzione
area_rettangolo(base, altezza)che restituisce l'area, e unaperimetro_rettangolo(base, altezza)che restituisce il perimetro. Stampale per un paio di rettangoli. - Scrivi una funzione
applica_sconto(prezzo, percento = 10)con uno sconto predefinito del 10%, e provala sia con lo sconto predefinito sia con uno diverso passato per nome. - Usando
filtrae una funzione anonima, estrai da una lista di numeri solo quelli maggiori della loro media. (Suggerimento: calcola prima lamedia, poi filtra.)