2011-01-12 10 views
7

Estoy implementando una versión rudimentaria de LISP en Ruby solo para familiarizarme con algunos conceptos. Estoy basando mi implementación en Lispy de Peter Norvig (http://norvig.com/lispy.html).Ayúdame a escribir mi LISP :) Entornos LISP, Ruby Hashes

Hay algo que me falta aquí, sin embargo, y te agradecería un poco de ayuda ...

Él subclases dict del pitón de la siguiente manera:

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) 

Luego pasa a explicar por qué hace esto en lugar de solo usar un dict. Sin embargo, por alguna razón, su explicación sigue pasando a través de mis ojos y fuera de mi cabeza.

Por qué no utilizar un dict, y luego dentro de la función eval, cuando se necesita crear un nuevo "subentorno", simplemente tome el dict existente y actualice los pares de clave/valor que necesitan actualizarse, y pase esa nueva ley en la próxima evaluación?

¿El intérprete de Python no hará un seguimiento de los envs "externos" anteriores? ¿Y la naturaleza de la recursión no garantizará que los valores se extraigan de "interno" a "externo"?

Estoy usando Ruby, y traté de implementar las cosas de esta manera. Algo no funciona, y podría ser por esto, o tal vez no. Aquí está mi función eval, siendo env un hash normal:

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 línea de que los asuntos de curso es el "lambda" uno.

Si hay una diferencia funcional, ¿qué hay de diferente entre lo que estoy haciendo aquí y lo que hizo Norvig con su clase Env? ¿Puede alguien describirme un caso en el que los dos se desviarán?

Si no hay diferencia, entonces quizás alguien me pueda aclarar por qué Norvig usa la clase Env. Gracias :)

+0

¡La mejor forma de entender Scheme es implementar Scheme! – erjiang

+0

Oh, tan cierto ... :) – MikeC8

Respuesta

5

Si los enlaces variables en su Lisp son inmutables, entonces los entornos de copiado equivalen a vincularlos. Sin embargo, en el siguiente caso:

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

(main) 

Si increment-i 'entorno s es completamente independiente de main' s (ya que es una copia), la mutación de i no será visible en main y el código anterior devolverá 0 . Si, por otro lado, los entornos están vinculados, el valor de retorno será 1, como se esperaba.

+0

Ah, creo que es eso. ¡Gracias! – MikeC8

1

Mi Python no es tan bueno y mi Lisp está bastante oxidado, pero voy a adivinar lo que está pasando: no se puede alejar de los punteros, incluso en los idiomas que afirman no tenerlos.

El eval del que está hablando no hará una copia del dict, solo hará una copia de la referencia y terminará con dos referencias (variables AKA) que se refieren a (es decir, un punto at) el mismo objeto subyacente. Lo que sucede es básicamente una variante de este:

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

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

Su clase Env permite el medio ambiente a ser modificada dentro del eval sin modificar el entorno exterior, mientras que el método find le permite acceder al medio ambiente exterior sin necesidad de saber acerca; también, las modificaciones terminarán dentro del entorno interno y esos cambios enmascararán los valores correspondientes en el entorno externo; las operaciones de obtención tendrán acceso al entorno local y al entorno exterior, las operaciones de configuración solo modificarán el entorno interno.

Supongo que puede llamar al Env una fachada de nivel de objeto (en lugar de nivel de clase).

No estoy seguro de qué pasa con su implementación de ruby, parece que está haciendo una copia modificada del hash de entorno. ¿Puedes aclarar "Algo no funciona"?

Aclaración: Env es una tabla de símbolos. El material de envoltura/redirección en find le permite acceder a la tabla de símbolos externa mientras la protege de los nuevos símbolos que se agregan, los nuevos símbolos solo se agregarán a la tabla de símbolos interna. Env esencialmente maneja el cierre.

+0

Gracias por su respuesta, tiene sentido lógico. Sin embargo, no creo que este sea el caso en este caso, ya que AFAIK "fusionar" en Ruby crea un nuevo Hash y deja intacto el anterior. Alguien me corrige aquí si estoy equivocado :) – MikeC8

+0

Ruby's Hash # merge devuelve un nuevo hash (http://www.ruby-doc.org/core/classes/Hash.html#M000759) así que tomaré esa parte fuera de mi respuesta. Tal vez podrías ser un poco más específico que "Algo no funciona". –

+0

Bueno, no vale la pena entrar en eso ... En realidad, no estoy preguntando cómo arreglar la parte del interp. eso no está funcionando, más bien tengo curiosidad sobre por qué Norvig hizo esa clase Env. – MikeC8