【问题标题】:Why does this not evaluate in Scheme?为什么这不在 Scheme 中进行评估?
【发布时间】:2018-03-07 01:19:04
【问题描述】:

我正在使用 DrRacket 环境来试用 Scheme 语言。

我将 sum+1 定义如下:

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

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

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

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

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

【问题讨论】:

标签: functional-programming scheme expression lisp racket


【解决方案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 发生变异并不明显,而且许多编译 Scheme 实现需要以不同方式处理变异的变量,因此它需要在代码运行之前知道 v需要特殊处理,但无法确定,因为(set! v 10) 可能来自数据库或用户输入。因此,通过不包括本地绑定,您可以为自己省去很多麻烦,并且语言更容易优化和编译。

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

【讨论】:

    【解决方案2】:

    带有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 定义并能够被打印。如有任何问题或反馈,请回复!

    【讨论】:

    • 非常感谢您的回答,因此“eval”在调用它的范围内看不到定义,而只能看到全局范围内的定义。我觉得这种行为很奇怪,因为其他过程可以访问覆盖全局范围的本地范围,并且由于 eval 本身是一个过程而不是特殊形式,它的行为应该像所有其他过程一样!是否有理由证明这种行为是正当的?
    • 我不完全确定为什么 eval 的行为不像其他过程,但这是我最好的猜测:eval 是一个特定的函数,要求对某些东西进行评估,所以有可能改为询问(像其他功能一样),“我可以获得什么信息来解决这个问题?”它问:“为了解决这个问题,正在向我提供什么信息?意思是,它需要给它特定的指令,而不是全局调用的过程。 (对不起,如果这没有多大意义,在我的脑海里听起来很合理。)
    • 值得思考eval 可以 看到词法绑定的实现如何工作:特别是(eval (read)) 应该如何工作?编译器是如何工作的?
    猜你喜欢
    • 2012-08-23
    • 2020-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多