为什么不在Scheme中评估?

时间:2017-09-26 00:06:28

标签: functional-programming scheme expression lisp racket

我正在使用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)

2 个答案:

答案 0 :(得分:0)

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

在您的示例中,您定义了:

(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定义并且可以打印。如有任何问题或反馈,请回复!

答案 1 :(得分: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语言只能被解释,因为它允许将宏作为第一类对象传递。这些语言在编译时无法推理。