Puo’ capitare di dover effettuare un’iterazione su piu’ elementi per volta in una sequenza.
Ad esempio, per creare una galleria di immagini, avrebbe senso avere accesso contemporaneamente sia ad un elemento i che agli elementi i+1 e i-1, per poter creare dei link tipo “< prev” e “next >“
In un linguaggio C-like si userebbe l’indice i esplicitamente, ma come gestire questo in un linguaggio dove l’iterazione è implicita? Un metodo sarebbe quello di usare una variabile esterna al ciclo:
>> count=0 => 0 >> ary= [1,2,3,4,5,6,7,8] => [1, 2, 3, 4, 5, 6, 7, 8] >> for i in ary >> print ary[count-1],i,ary[count+1],"\n" >> count+=1 >> end 812 123 234 345 456 567 678 78nil => [1, 2, 3, 4, 5, 6, 7, 8]
Questo metodo è poco elegante ed inconsistente, ad esempio nel primo ciclo dovremmo controllare che si tratti del primo, per non usare un elemento con indice minore di zero, e nell’ultimo per non usarne uno fuori dall’Array.
Ma ruby non e’ un linguaggio inconsistente ed inelegante, dunque vediamo come miglirare un po’ la cosa.
Anzitutto, possiamo usare each_with_index(), e liberarci di quella brutta variabile esterna:
>> ary.each_with_index do |el,i| ?> print ary[i-1],el,ary[i+1],"\n" >> end 812 123 234 345 456 567 678 78nil => [1, 2, 3, 4, 5, 6, 7, 8]
Leggermente meglio, na ancora abbiamo il problema di controllare il ciclo iniziale e finale.. Sarebbe meglio se potessimo dire a ruby: “dammi n elementi consecutivi”.
In effetti, possiamo farlo, usando il modulo enumerator, incluso nella libreria standard:
>> require 'enumerator' => true >> ary.each_cons(3) do |pre,el,suc| ?> print pre,el,suc,"\n" >> end 123 234 345 456 567 678 => nil
Perfetto!
Ma cosa e’ successo ? Semplice, enumerator aggiunge alcuni metodi utili al modulo Enumerable e di conseguenza alla classe Array.
Tra questi metodi esiste il comodissimo each_cons( ) che richiama il blocco ogni volta con una sequenza di n elementi, in questo caso n=3.
Nessun bisogno di gestire casi particolari o di scrivere codice brutto ed error prone, abbiamo tutto gia’ pronto.