In questo capitolo faremo una panoramica sui blocchi e sugli iteratori, in modo da avere gli strumenti necessari per affrontare i prossimi capitoli. Anche se sono caratteristiche presenti in altri linguaggi da lungo tempo, è il Ruby che ha reso l'utilizzo di questi potenti strumenti incredibilmente semplice.
Blocchi e iteratori
Iniziamo con un paio di definizioni. Un blocco sintatticamente è una porzione di codice compresa tra le parole chiave do
e end
oppure tra una coppia di parentesi graffe { e }; convenzionalmente si utilizza la seconda forma per blocchi che occupano una sola riga. Un iteratore invece è un normale metodo che accetta come argomento un blocco, ovvero una funzione anonima che viene eseguita sui parametri passatigli dall'iteratore. È molto più semplice di quello che sembra, vediamo un semplice esempio per chiarire un po' le cose:
> str = "Ciao Mondo" > str.each_byte {|c| puts c.chr} C i a o M o n d o
Abbiamo utilizzato l'iteratore each_byte
della classe String
che passa ogni byte della stringa come parametro al blocco tra parentesi graffe. Abbiamo anche passato un parametro (c
) tra il metodo e il blocco. La variabile c
, locale al blocco, assume quindi di volta in volta il valore passato dal metodo, nel nostro esempio i singoli caratteri delle stringa.
È possibile passare dei parametri anche all'iteratore come a qualsiasi altro metodo. Ad esempio l'iteratore each
, o each_line
, della classe String accetta come argomento il valore del separatore (n è il valore di default):
> str.each {|c| puts c} Ciao Mondo > str.each(' ') {|c| puts c} Ciao Mondo
Vedremo la grande utilità degli iteratori quando parleremo degli array e delle altre strutture dati.
Prima di concludere diamo un altro sguardo ai blocchi e vediamo come vanno chiamati dall'interno di un metodo. Subito un esempio che poi commenteremo:
def chiama puts "Sono nel metodo" yield end chiama { puts "Sono nel blocco" }
eseguendo questo codice otterremo il seguente output:
Sono nel metodo Sono nel blocco
Per richiamare il blocco abbiamo usato l'istruzione yield
, che non fa altro richiamare il blocco associato al metodo che lo contiene. È possibile utilizzare più volte yield all'interno di un metodo:
def chiama yield puts "Sono nel metodo" yield end
e anche passare parametri al blocco tramite yield
:
def chiama puts "Sono nel metodo" yield(100) end chiama { |n| puts "Sono nel blocco e il parametro vale: #{n}" }
In questo modo avremo in output
Sono nel metodo Sono nel blocco e il parametro vale: 100
Apriamo una breve parentesi sul significato di #{espressione}
all'interno della stringa passata a puts. In pratica il codice contenuto all'interno delle parentesi viene eseguito e il risultato trasformato in stringa, e nel nostro esempio il tutto è stampato.
Infine possiamo creare un blocco contenente delle normali espressioni:
begin #espressione1 #espressione2 #... end
Il valore dell'ultima espressione valutata sarà anche il valore del blocco. Come vedremo tra qualche capitolo begin/end
risulta di grande utilità soprattutto nella gestione delle eccezioni.