Merzia sta passando a Ruby per lo sviluppo delle prossime applicazioni. Abbiamo scelto di utilizzare mod_ruby con eruby, invece che il famoso framework Ruby on Rails, perche' la nostra filosofia di sviluppo e' quella di fare e usare cose semplici, con poche dipendenze, in cui abbiamo tutto il controllo del codice. In breve stiamo portando elementi del nostro mini-framework dal PHP al Ruby.
Insomma per questi motivi per ora gioco abbastanza con Ruby, ieri e' stata la volta di
callcc, che esattamente come in Scheme chiama una funzione passando a questa la
continuazione corrente del programma.
Cosa significa di preciso? Prendiamo questo codice per riferimento:
continuazione=nil
flag=nil
(1..10).each {|x|
puts x
if x == 7
callcc {|c| continuazione=c}
end
}
if flag == nil
flag=true
continuazione.call
end
Il codice "conta" da 1 a 10, ma quando x ha il valore
di 7 tramite callcc viene chiamata la funzione anonima
(o blocco che dir si voglia)
{|c| continuazione=c}
che prende un argomento, c, che contiene la continuazione corrente,
e la salva nella variabile
continuazione.
Per il resto il ciclo continua normalmente.
Alla fine, se non e' gia' stato fatto in precedenza
(serve a questo il
flag), tale continuazione viene chiamata
tramite il suo metodo call. Il risultato e' che
il programma ricomincia da dove era arrivato.
L'output del programma e' il seguente
1
2
3
4
5
6
7
8
9
10
8
9
10
Si noti la ripetizione di 8, 9, 10. Siamo andati indietro nel tempo
per un attimo col programma :) Il flag e' utile perche' altrimenti
il programma entrerebbe in un loop infinito, perche' dopo aver scritto
nuovamente 8, 9, 10 ... la continuazione verrebbe chiamata ancora una
volta.
Non e' magia... per chi capisce un po' di implementazione di linguaggi
di programmazione quello che succede e' che
callcc salva lo
stack frame in un oggetto, e il suo metodo
call non fa altro
che ripristinare lo stack frame precedentemente salvato.
Ovviamente nello stack frame ci sono solo le variabili locali, dunque
gli attributi degli oggetti (o variabili di istanza se preferite),
cosi' come le variabili globali non vengono ne salvate ne ripristinate
dalla continuazione.
Insomma per farla breve ieri ho scritto un po' di codice per giocare con
questa idea che simula i generatori di Python. Ecco il codice:
class Methodgen
def initialize(f,*args)
@f = f
@args = args
@gencont = nil
@more = true
end
def next
if @more == false
return nil
end
if (@gencont == nil)
@gencont=@f
end
callcc {|@retcont|
@gencont.call(*@args) {|@ele|
callcc {|@gencont|
@retcont.call
}
}
}
if @ele == nil
@more = false
end
@ele
end
end
def foobar(incr)
(1..10).each {|x|
yield incr+x
}
yield nil
end
a = Methodgen.new(method(:foobar),5);
b = Methodgen.new(method(:foobar),50);
puts a.next
puts b.next
puts a.next
puts b.next
puts a.next
puts b.next
Inutile spiegarlo perche' richiederebbe un articolo intero ;) Ma chi
vuole giocare con queste idee forse potrebbe trovare in questo pezzo
di codice uno spunto di approfondimento.
In ogni caso l'output del programma e' il seguente:
6
51
7
52
8
53
Ruby e' molto interessante, flessibile e leggero, e anche se alcune cose sembrano un po' arbitrarie mi sembra la migliore alternativa in giro attualmente.