2011-07-08 17 views
6

Esto parece una pregunta simple; quizás es tan simple que es difícil encontrar una búsqueda que encuentre la respuesta. En Scheme (específicamente, la implementación de Guile si hace alguna diferencia) ¿cómo puedo evaluar algo que ha sido citado?control de evaluación en el esquema (engaño)

Esto es lo que estoy tratando de hacer.

Básicamente, necesito asegurarme de que una función que defino obtiene sus argumentos evaluados en un orden específico, porque los efectos secundarios causados ​​por la evaluación de un argumento dependen de la evaluación de otros argumentos. Sin embargo, Scheme dice que los argumentos se pueden evaluar en cualquier orden, por lo que quiero forzarlo manualmente citando los argumentos y luego evaluarlos manualmente en el orden que se necesita.

Parece que "eval" es supone a hacer lo que quiera, pero tiene dos problemas:

  1. Su uso no es recomendable, así que me siento que debe haber una mejor manera de lograr lo Quiero hacer aqui.
  2. En Scheme parece que eval toma un segundo parámetro que es el entorno. Esto me confunde. Quiero que evalúe en el mismo entorno en el que aparece la declaración, entonces, ¿por qué debería necesitar un segundo parámetro? ¿Esto es posible? He jugado con eval un poco y parece que algunas implementaciones requieren diferentes parámetros (por ejemplo, mit-scheme ni siquiera sabe qué (interacción-entorno) es !!!)

He intentado otros trucos , como construir una lambda:

(list 'lambda '() '(car (b c))) 

pero parece que esto tendría que evaluarse para generar un procedimiento. También probé:

(list lambda '() '(car (b c))) 

pero esto devuelve un "primitivo-orden interna-macro" que no funciona bien.

Editar: Parece que una macro trabajará para la orden de la evaluación controlar: (defmacro test1 (ab) '(comenzar, B, A))

Respuesta

1

Si es necesario evaluar una estructura de lista (listas anidadas con símbolos entre comillas que representan un texto del programa de Esquema), entonces debe usar eval. Esquema requiere pasar un entorno como segundo argumento, incluso si es el entorno actual:

(eval '(+ x y) (interaction-environment)) 

Si sólo tiene que hacer sus cálculos en un orden determinado, puede hacer cumplir la orden de evaluación de los efectos secundarios mediante el uso de begin, let, o simplemente un cuerpo de función.Definen una secuencia de evaluaciones:

(let ((x 42)) 
    ; eval with effects #1 
    (display x) 
    ; eval with effects #2 
    (display (+ x 1))) 

Editar: Si es necesario tener un bloque parametrizada de código en el que puede pasar expresiones no evaluadas y luego forzar su evaluación en algún orden particular, entonces puede usar uno de estas técnicas:

  • Una macro (como se ha mencionado ya, simplemente para la corrección):

    > (defmacro test1 (a b) `(begin ,b ,a)) 
    > (test1 (display 2) (display 3) 
    32 
    
  • Un cálculo (sintaxis especial del Esquema para la evaluación perezosa) retrasó:

    > (define (test1 a b) (begin (force b) (force a))) 
    > (test1 (delay (display 2)) (delay (display 3))) 
    32 
    
  • Una abstracción lambda regular y aplicación

    > (define (test1 a b) (begin (b) (a))) 
    > (test1 (lambda() (display 2)) (lambda() (display 3))) 
    32 
    
+0

¿Cómo funciona la macro si necesito una cantidad variable de argumentos? '(defmacro test1 (a. b) \' (begin, b, a)) 'no funciona porque b ahora es una lista. Necesito unirlo de alguna manera al principio, pero varios intentos como '\' (begin (if (pair?, B) (test1, b)), a)) 'no funcionan. – Michael

+0

también, no puedo usar '(defmacro test1 (a. B) \' (begin (apply begin, b), a)) 'porque no puedo aplicar una macro. – Michael

+0

el que te perdiste es '(defmacro test1 (a. B) \' (begin, (cons 'begin b), a)) ' –

8

eval es totalmente la herramienta equivocada para sólo cambiar el orden de evaluación de argumentos. Crear una macro en su lugar:

;; (my-fun e1 e2) 
;; Just calls my-real-fun, but evaluates e2 before e1 
(define-syntax my-fun 
    (syntax-rules() 
    [(my-fun e1 e2) 
    ;; let* has guaranteed order of evaluation 
    (let* ([y e2] 
      [x e1]) 
     (my-real-fun x y))])) 

(define (my-real-fun x y) ....) 

O utilice defmacro, si es necesario.

0

Estabas en el camino correcto al pasar en lambdas. Si tiene

(define (f x y z) ...) 

... entonces se le puede llamar así:

(f 
    (lambda() a) 
    (lambda() b) 
    (lambda() c)) 

Para ello, será f con todos los argumentos (a, b, c) en una forma sin evaluar. Dentro de f, tiene completa potencia para elegir el orden en que los evalúa. La única diferencia es que tiene que llamar explícitamente al (x), (y) y (z), y capturar sus valores dentro de define o let -like statements. Esto le permite asegurarse de que los efectos secundarios ocurran solo una vez.

No hay necesidad de macros aquí en absoluto. Por cierto, no te preocupes por usar muchas lambdas en todas partes, son muy baratas.