2011-01-12 8 views
7

Sto implementando una versione rudimentale di LISP in Ruby solo per familiarizzare con alcuni concetti. Sto basando la mia implementazione su Lispy di Peter Norvig (http://norvig.com/lispy.html).Aiutami a scrivere il mio LISP :) Ambienti LISP, Ruby Hashes

C'è qualcosa che mi manca qui, però, e mi farebbe piacere un po 'di aiuto ...

Egli sottoclassi dict di Python come segue:

class Env(dict): 
    "An environment: a dict of {'var':val} pairs, with an outer Env." 
    def __init__(self, parms=(), args=(), outer=None): 
     self.update(zip(parms,args)) 
     self.outer = outer 
    def find(self, var): 
     "Find the innermost Env where var appears." 
     return self if var in self else self.outer.find(var) 

Si passa poi a spiegare perché lo fa questo piuttosto che usare semplicemente un ditt. Tuttavia, per qualche ragione, la sua spiegazione continua a passare attraverso i miei occhi e fuori attraverso la parte posteriore della mia testa.

Perché non utilizzare un dict e quindi all'interno della funzione eval, quando è necessario creare un nuovo "sub-environment", basta prendere il dict esistente e aggiornare le coppie chiave/valore che devono essere aggiornate e passare quel nuovo dettato alla prossima eval?

L'interprete Python non tiene traccia dei precedenti "esterni"? E la natura della ricorsione non garantirà che i valori vengano estratti da "interiore" a "esterno"?

Sto usando Ruby e ho cercato di implementare le cose in questo modo. Tuttavia, qualcosa non funziona, e potrebbe essere a causa di ciò, o forse no. Ecco la mia funzione eval, ENV essere un Hash regolare:

def eval(x, env = $global_env) 
    ........ 
    elsif x[0] == "lambda" then 
    ->(*args) { eval(x[2], env.merge(Hash[*x[1].zip(args).flatten(1)])) } 
    ........ 
end 

La linea che conta, naturalmente, è il "lambda" uno.

Se c'è una differenza funzionale, cosa c'è di diverso tra ciò che sto facendo qui e ciò che Norvig ha fatto con la sua classe Env? Qualcuno può descrivermi un caso in cui i due si discosteranno?

Se non c'è differenza, forse qualcuno può illuminarmi sul perché Norvig usi la classe Env. Grazie :)

+0

Il modo migliore per comprendere Scheme è implementare Scheme! – erjiang

+0

Oh, così vero ... :) – MikeC8

risposta

5

Se i legami variabili nel Lisp sono immutabili, copiare gli ambienti equivale a collegarli. Ma si consideri il seguente caso:

(define main 
    (lambda() 
    (define i 0) 
    (define increment-i 
     (lambda() 
     (set! i (+ i 1)))) 
    (increment-i) 
    i)) 

(main) 

Se increment-i 'ambiente s è completamente indipendente main' s (dal momento che è una copia), la mutazione di i non sarà visibile nel main e il codice di cui sopra tornerà 0 . Se, al contrario, gli ambienti sono collegati, il valore restituito sarà 1, come previsto.

+0

Ah, credo che sia così. Grazie! – MikeC8

1

My Python non è buono e il mio Lisp è piuttosto arrugginito, ma dovrò indovinare cosa sta succedendo: non puoi allontanarti dai puntatori anche in lingue che pretendono di non averli.

Il eval di cui si sta parlando non eseguirà una copia dello dict, farà solo una copia del riferimento e si finirà con due riferimenti (variabili AKA) a cui fare riferimento (cioè punto a) lo stesso oggetto sottostante. Ciò che accade è sostanzialmente una variante di questo:

a = [ 'a', 'b', 'c' ] 
b = a 
a[1] = 'x' 

puts a 
# => ["a", "x", "c"] 
puts b  
# => ["a", "x", "c"] 

sua classe Env permette l'ambiente da modificare all'interno del eval senza modificare l'ambiente esterno, mentre il metodo find permette di accedere all'ambiente esterno senza la necessità di conoscere; inoltre, le modifiche finiranno all'interno dell'ambiente interno e quelle modifiche maschereranno i valori corrispondenti nell'ambiente esterno; ottenere operazioni accederà all'ambiente locale e all'ambiente esterno, le operazioni di impostazione modificheranno solo l'ambiente interno.

Immagino che si possa chiamare Env una facciata a livello di oggetto (piuttosto che a livello di classe).

Non sono sicuro di cosa c'è che non va nell'implementazione di Ruby, sembra che tu stia facendo una copia modificata dell'hash dell'ambiente. Puoi chiarire "Qualcosa non funziona però"?

Precisazione: Env è una tabella dei simboli. Il materiale di avvolgimento/reindirizzamento in find consente di accedere alla tabella dei simboli esterna mentre la protegge dai nuovi simboli che vengono aggiunti, i nuovi simboli verranno aggiunti alla tabella dei simboli interna. Env gestisce essenzialmente la chiusura.

+0

Grazie per la tua risposta, ha senso logico. Tuttavia, non credo che questo sia il caso in questo caso, poiché AFAIK "si fonde" in Ruby crea un nuovo hash e lascia intatto quello vecchio. Qualcuno mi corregge qui se sbaglio :) – MikeC8

+0

L'unione di Ruby's Hash restituisce un nuovo Hash (http://www.ruby-doc.org/core/classes/Hash.html#M000759) quindi prenderò quella parte fuori dalla mia risposta. Forse potresti essere un po 'più specifico di "Qualcosa non funziona però". –

+0

Beh, non vale la pena di parlarne ... In realtà non sto chiedendo come riparare la parte dell'interp. non funziona, anzi, sono principalmente curioso di sapere perché Norvig ha fatto quella classe di Env. – MikeC8