2009-03-17 7 views
8

Estoy escribiendo un intérprete de Scheme, y yo estoy frente a una sentencia let válido, como:evaluación Lisp de dejar que las declaraciones

;; should print 7 
(let ((a 4) (b 3)) 
    (let ((a (* a a)) 
      (b (* b b))) 
     (+ a b) 
     (- a b))) 

Mi intérprete desarrolla tan sólo un subconjunto puramente funcional del esquema, por lo que no habrá efectos secundarios como set !. En un lenguaje puramente funcional, ¿por qué permitirías múltiples expresiones dentro de una instrucción let como la anterior?

Y al escribir mi intérprete, ¿hay alguna razón por la que debería evaluar algo más que la última expresión en el let? Parece que nunca podrían afectar el resultado de la última declaración evaluada.

Respuesta

2

Tienes razón (casi): si va a implementar un subconjunto puramente funcional del Esquema (es decir, sin set!, set-car!, set-cdr!) entonces cualquier expresión, sino el último de una let tendrán su valor de retorno tira, y ya que está garantizado que no tendrá efectos secundarios, no hay peligro en ignorarlos silenciosamente.

Sin embargo, hay un pequeño caso debe tener en cuenta, y es entonces cuando las expresiones anteriores son define s:

(let ((x 3)) 
    (define y 4) 
    (+ x y)) 

Esto es legal y funcional. Sin embargo, hay algunas buenas noticias: dentro de un bloque (como un let) debe tener todos sus define s en la parte superior. Al igual que en, esto no se considera Esquema legal:

(let ((x 3)) 
    (+ 2 3) 
    (define y 4) 
    (+ x y)) 

Esto significa que cuando se evalúa un bloque, todo lo que tiene que hacer es escanear la parte superior de define s y envolverlos en el letrec expresión equivalente, a continuación, proceder a ignora todo excepto la última expresión (que luego regresarías).

editar:antti.huima es un excelente punto sobre call/cc.Si va a incluir continuaciones en su implementación, realmente no puede hacer muchas suposiciones sobre cuándo se evaluarán las cosas.

1

De acuerdo, el let está creando un enlace, como define. No hay nada que esté cambiando una variable enlazada como set!. Entonces, piense en lo que el alcance de sus nombres es: ¿es el a del '(+ a b) the same as the a` usted obligado a 4? (Pista:. No)

El verdadero punto aquí es que es necesario que se comporte correctamente incluso en casos hinky como éste: la determinación del alcance y normas de obligado cumplimiento son simples y bien definido, y hacer algo como esto, que ve confuso, es solo una consecuencia de ellos. Es conveniente, porque al tener enlaces de ámbito léxico local con let, puede escribir programas más claros, incluso si hay casos secundarios perversos.

actualización Oh, dejé un punto. Tiene razón en que la invocación (+ a b) no tiene un efecto duradero, pero en el caso general no puede suponer que eso sería cierto, y no puede determinar si es cierto examinando el texto del programa solo. (Considere: podría haber otras funciones allí en lugar de "+"). Sin embargo, en el resto, si cree que obtendrá el resultado correcto sin evaluar las diversas cláusulas let, no comprende lo que está tratando de hacer. hacer aún.

6

en realidad no se puede "soltar" todo excepto la última declaración, porque las declaraciones anteriores podrían no ser definitivas. Por ejemplo:

(define (func) (func)) 

(let() 
    (func) ;; does not return 
    1) 

Aquí si se deja sin evaluar (func), se obtiene un resultado erróneo (que es 1), mientras que usted debe conseguir cómputo no terminar.

Otra cuestión es que la llamada/cc (llame-con-corriente de continuación) (y sí, pertenece al subconjunto funcional) se puede utilizar para realmente retorno del cálculo de la posición de no-cola, por ejemplo:

(call-with-current-continuation 
    (lambda (ret) 
    (let() 
     (ret 3) 
     4))) 

que devolverá y no 4. Este es todavía puramente funcional.

Nota Por cierto que (let() x y z) es equivalente a la forma de un único estado (let() (begin x y z)) así que la pregunta real es si necesita begin :)

Cuestiones relacionadas