2017-09-26 93 views
1

我正在使用DrRacket环境来尝试Scheme语言。为什么在Scheme中不评估?

我定义的总和+ 1如下:

(define sum+1 '(+ x y 1)) 

我想知道为什么下面的表达式不计算:

(let ([x 1] [y 2]) (eval sum+1)) 

,而这样做返回正确的值:

(define x 1) 
(define y 2) 
(eval sum+1) 
+0

这在[球拍指南](https://docs.racket-lang.org/guide/eval.html)中有相当好的解释。 – molbdnilo

回答

0

eval没有,除非是词法变量是在同一个表达式创建了词法变量的工作:

#!r7rs 
(import (scheme base) 
     (scheme eval)) 

(define env (environment '(scheme base))) 

(let ((x 10)) 
    (eval 'x env)) ; ERROR! `x` is not defined 

你可以把它看作eval总是在发生顶层与环境的全局绑定你传递给第二个参数。你可以从你的词法环境值传递这样也许绝招吧:

(eval '(let ((x 10)) 
     x) 
     env) ; ==> 10 


(let ((x 10)) 
    (eval `(let ((x ,x)) 
      x) 
     env) ; ==> 10 

的时候最Scheme实现运行代码局部变量通常堆栈中分配。因此,一些想象这样的代码:

(define (test v) 
    (display v) 
    (newline) 
    (eval 'v)) 

可能会在运行时变成这样:

(define (test 1 #f) ; indicates 1 argument, no rest 
    (display (ref 0)) ; fetches first argument from stack 
    (newline) 
    (eval 'v))  ; but what is v?, certainly not the first argument 

您也可以使角落的情况。如果你变异会发生什么?

(define (test v) 
    (eval '(set! v 10)) 
    v) 

eval结构可能来自用户的输入,所以它不是很明显,v被突变,也有很多编制方案的实施需要那么它需要知道的代码运行的是v需要特殊的前处理该变异不同的变量治疗,但它不是可确定的,因为(set! v 10)可能来自数据库或用户输入。因此,通过不包含本地绑定,您可以节省很多麻烦,并且语言更容易优化和编译。

有Lisp语言只能解释,因为它允许传递宏作为第一类对象。这些语言在编译时无法推理。

0

let不起作用的命令的原因是let创建局部变量。这意味着它创建的变量不能从任何地方访问 - 只能从let的正文参数中访问。

在你的榜样,你定义:

(define sum+1 '(+ x y 1)) 

然后,你就控制了:

(let ([x 1] [y 2]) (eval sum+1)) 

这不起作用,因为xy只被在eval声明中定义的,而不是内程序sum+1。这看起来可能与直觉相反,但它可以防止其他输入出现很多错误。

你的第二个例子是:

(define x 1) 
(define y 2) 
(eval sum+1) 

这不工作,因为xy是全局定义。这意味着他们可以在任何地方和任何地方访问。然后将它们应用于sum+1定义并可以打印。请回答您的任何问题或反馈!

+0

非常感谢您的回答,因此'eval'无法在所调用的范围内看到定义,而只能看到全局范围内的定义。我发现这种行为很奇怪,因为其他过程可以访问覆盖全局范围的本地范围,并且由于eval本身是一个过程而不是特殊形式,它应该像所有其他过程一样行为!有没有理由证明这种行为? –

+0

我不完全确定为什么eval不像其他程序那样行事,但这是我最好的猜测:eval是一个特定的函数,要求对某些东西进行评估,所以有可能不是询问(就像其他函数一样),“我有什么信息可以解决这个问题?”它问道:“我给了什么信息来解决这个问题?”意思是,它需要特定的指令,而不是全局调用的过程。 (对不起,如果这没有什么意义,我的脑海中听起来很合理。) –

+1

值得考虑一下'eval' *能够*看到词法绑定的实现是如何工作的:特别是应该如何'(eval(读))工作?编译器如何工作? – tfb

相关问题