以下球拍功能会产生错误:
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) ))
答案 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)
我认为,对于您想要做的事情,在表达式 - 组合时设置val
,row
和col
的值可能就足够了,而不是口译员抓住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)