eval使用当前函数的命名空间

时间:2011-02-15 02:32:29

标签: scheme eval racket

以下球拍功能会产生错误:

reference to undefined identifier: val

这是因为eval函数查看全局命名空间,而不是本地函数的命名空间。如何欺骗eval使用本地函数的命名空间?

(define some-eval!
  (lambda (val row col)
    (eval (list 'define 'ttboard '(list-builder val row col))) (current-namespace) ))

4 个答案:

答案 0 :(得分:3)

您的问题仅适用于Racket。通常,不同的Scheme实现有各种解决此问题的方法,但在几乎所有情况下,您都不会像eval那样处理本地绑定,就像您正在尝试的那样。但是针对Racket案例,你应该阅读Racket指南中的evaluation section - 它解释了为什么事情不像你想要的那样工作,它显示了如何使模块范围工作。

就像快速摘要一样 - eval无法看到本地绑定的原因是这意味着(lambda (x) x)(lambda (y) y)无法编译到同一个函数,因为名称可以有所作为。您可能会认为编译可能取决于函数内部是否使用eval - 但这是在编译时无法确定的。例如:

(define (foo f) (let ([x 1]) (f 'x)))
(foo eval)

在这种情况下,编译器无法告诉foo将使用eval进行调用。

最后,尝试解决eval的其他语言也存在类似的困难。例如,在JS中,eval是一个可以影响函数编译方式的神奇之物,如下所示:

function foo(x) { alert("x = " + eval("x")); }

实际上会起作用 - 但它需要eval实际存在于函数体中。如果没有这样做,那么事情就会破裂。例如,此代码:

function foo(x,f) { alert("x = " + f("x")); }
foo(123,eval);

在Fx中适合我,但在Chrome中失败,说x is not defined。如果这还不足以证明这个混乱,请考虑一下:

function foo(x,f) { alert("x = " + f("x")); }
var x = 456;
foo(123,eval);

在Chrome 中显示456,在Fx中显示

答案 1 :(得分:1)

我认为,对于您想要做的事情,在表达式 - 组合时设置valrowcol的值可能就足够了,而不是口译员抓住val等人的价值观。在评估时间(在Racket中不可能)。

(define some-eval!
  (lambda (val row col)
    (eval (list 'define 'ttboard `(list-builder ,val ,row ,col)))
          (current-namespace)))

请注意,几乎可以肯定有一种更好,更清晰的方法可以在没有eval的情况下完成您要执行的操作,当然,除非您使用eval进行练习。

答案 2 :(得分:1)

您可以提前定义ttboard,然后set!

(define ttboard #f)
(define create-board
  (lambda (val row col)
    (set! ttboard (list-builder val row col))))

这样,您可以清楚地告诉ttboard是一个全局变量,而不是在eval'd子句中隐藏其定义。

答案 3 :(得分:1)

正如Eli指出的那样,一般情况下,eval无法访问词汇环境。但是,我相信这种解决方法可以满足您的需求:

#lang racket/load

(define some-eval!
  (lambda (val row col)
    (namespace-set-variable-value! 'val val)
    (namespace-set-variable-value! 'row row)
    (namespace-set-variable-value! 'col col)
    (eval (list 'define 'ttboard '(list-builder val row col)) 
          (current-namespace))))
(define list-builder list)
(some-eval! 1 2 3)
(display ttboard)