我正在使用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)
我正在使用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)
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语言只能解释,因为它允许传递宏作为第一类对象。这些语言在编译时无法推理。
与let
不起作用的命令的原因是let
创建局部变量。这意味着它创建的变量不能从任何地方访问 - 只能从let
的正文参数中访问。
在你的榜样,你定义:
(define sum+1 '(+ x y 1))
然后,你就控制了:
(let ([x 1] [y 2]) (eval sum+1))
这不起作用,因为x
和y
只被在eval
声明中定义的,而不是内程序sum+1
。这看起来可能与直觉相反,但它可以防止其他输入出现很多错误。
你的第二个例子是:
(define x 1)
(define y 2)
(eval sum+1)
这不工作,因为x
和y
是全局定义。这意味着他们可以在任何地方和任何地方访问。然后将它们应用于sum+1
定义并可以打印。请回答您的任何问题或反馈!
非常感谢您的回答,因此'eval'无法在所调用的范围内看到定义,而只能看到全局范围内的定义。我发现这种行为很奇怪,因为其他过程可以访问覆盖全局范围的本地范围,并且由于eval本身是一个过程而不是特殊形式,它应该像所有其他过程一样行为!有没有理由证明这种行为? –
我不完全确定为什么eval不像其他程序那样行事,但这是我最好的猜测:eval是一个特定的函数,要求对某些东西进行评估,所以有可能不是询问(就像其他函数一样),“我有什么信息可以解决这个问题?”它问道:“我给了什么信息来解决这个问题?”意思是,它需要特定的指令,而不是全局调用的过程。 (对不起,如果这没有什么意义,我的脑海中听起来很合理。) –
值得考虑一下'eval' *能够*看到词法绑定的实现是如何工作的:特别是应该如何'(eval(读))工作?编译器如何工作? – tfb
这在[球拍指南](https://docs.racket-lang.org/guide/eval.html)中有相当好的解释。 – molbdnilo