Translate To English!
Stavo dando una occhiata al blog di Kazimir (http://kazimirmajorinc.blogspot.com/), uno dei miei blog preferiti per newLisp.
E’ veramente bravo, e delle volte pubblica degli articoli che mi fanno impazzire (belli, originali, semplici e geniali!).
Uno di questi è intitolato… Crawler Tractor. …
Cosa? Come dite? Cosa c’entra questo con newLisp… ora ve lo spiego…
newLisp delle volte permette di fare delle cose veramente curiose, e questa è una! Nell’esempio è stata realizzata una funzione NON ricorsiva e senza loop, ma in grado di funzionare e produrre risultati indefinitivamente. Il codice è semplicissimo (pubblico quello mostrato da Kazimir), poi vi spiegherò come funziona (anche se il programma è molo piccolo da esso si possono trarre spunti e suggerimenti molto interessanti!):
(set 'f
(lambda()
(begin
(println "Hi for the " (inc counter) ". time. ")
(push (last f) f -1)
(if (> (length f) 3) (pop f 1) ) ) ) )
(f)
Il programma produrrà un risultato simile al seguente:
; Hi for the 1. time.
; Hi for the 2. time.
; Hi for the 3. time.
; Hi for the 4. time.
; Hi for the 5. time.
; Hi for the 6. time.
......... e così via ........
Come è possibile tutto ciò?
Analizziamo un paio di righe di codice che compiono la “magia”:
(push (last f) f -1)
La funzione (last f) ritorna il corpo della funzione stessa, cioè tutto ciò che si trova all’interno del (begin), begin compreso:
(begin
(println "Hi for the " (inc counter) ". time. ")
(push (last f) f -1)
(if (> (length f) 3) (pop f 1) ) ) ) )
Quindi, la funzione (push) non fà altro che inserire il corpo del programma dentro sè stessa. Questo è possibile grazie al dualismo codice<->dati che ha newLisp. Vi faccio un esempio più semplice:
> ( set 'f (fn () (println "ok") ) )
(lambda () (println "ok"))
> (push (last f) f -1)
(lambda () (println "ok") (println "ok"))
Come potete vedere, la funzione (push) appende alla funzione corrente, in esecuzione, un nuovo pezzo di codice, cioè il corpo della funzione stessa! GENIALE!
L’ultima riga (quella con la IF) serve per eliminare il codice “vecchio”, già eseguito e completato. NOTA: viene usato come termine di confronto il valore > 3 , questo per evitare di eliminare il pezzo di codice attualmente in esecuzione!
Sto quindi facendo qualcosa simile a ciò che “secoli” fà (scusate ma io sono “vecchietto”) si faceva in linguaggio macchina: si generava codice auto-generante. Infatti questa tecnica potrebbe essere utilizzata non sono per replicare n-volte codice esistente (come nel nostro caso), ma anche per generare/modificare al volo codice esistente.
Questo articolo mi ha fatto venire in mente anche ciò che si fà in Erlang, chiamando una funzione ricorsiva come ultima chiamata, la quale permette di implementare la ricorsività senza però andare ad intaccare lo stack (evitando spiacevoli stack-overflow) 
Come dite? Al termine di questo mini-articolo non avete comunque capito che cosa c’entra il trattore? Ditemelo voi… (o chiedetelo a Kazimir!).
A presto!
Commenti Recenti