使用当前函数的命名空间进行评估

发布于 2024-10-17 22:57:22 字数 305 浏览 1 评论 0原文

以下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) ))

The following racket function produces the error:

reference to undefined identifier: val

This is because the eval function looks at the global namespace, not the local function's namespace. How do I trick eval into using the local function's namespace?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧话新听 2024-10-24 22:57:22

你的问题是针对 Racket 的。一般来说,不同的Scheme实现有不同的方法来解决这个问题,但在几乎所有情况下,你都不会像你试图做的那样让eval来处理本地绑定。但具体到 Racket 案例,您应该阅读评估部分在 Racket 指南中——它解释了为什么事情没有像你希望的那样工作,并且它展示了如何让模块范围工作。

作为一个快速总结 - 本地绑定对 eval 不可见的原因是,这意味着 (lambda (x) x)(lambda (y ) y) 无法编译为相同的函数,因为名称可能有所不同。您可能会争辩说,编译可能取决于函数内部是否使用了 eval - 但这是无法在编译时确定的。例如:

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

在这种情况下,编译器无法判断将使用 eval 调用 foo

最后,其他尝试解决 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 未定义。如果这还不足以证明混乱,请考虑以下内容:

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

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

Your question is specific to Racket. In general different Scheme implementations have various approaches to this problem, but in almost all cases you will not get eval to handle local bindings like you're trying to do. But specific to the Racket case, you should read the evaluation section in the Racket guide -- it explains why things aren't working like you want them to, and it shows how to get the module scope to work.

Just as a quick summary -- the reason local bindings are not visible to eval is that this means that (lambda (x) x) and (lambda (y) y) cannot be compiled to the same function, since the names can make a difference. You could argue that the compilation could depend on whether eval is used inside the function or not -- but that's something that cannot be determined at compile-time. For example:

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

In this case, there is no way for the compiler to tell that foo is ever going to be called with eval.

Finally, there are similar difficulties in other languages that try to tackle eval. For example, in JS, eval is a magical thing that can affect the way a function is compiled, so something like:

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

will actually work -- but it requires eval to be physically present in the body of the function. If that's not done, then things can break. For example, this code:

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

works for me in Fx, but fails in Chrome, saying that x is not defined. And if that's not enough to demonstrate the mess, consider this:

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

shows 456 in Chrome and in Fx.

可是我不能没有你 2024-10-24 22:57:22

你可以提前定义ttboard,然后set!它:

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

这样,你就可以清楚地知道ttboard是一个全局变量,而不是其定义在 eval'd 子句中变得模糊。

You can define ttboard ahead of time and then set! it:

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

That way, you can clearly tell that ttboard is a global variable, rather than having its definition obscured in an eval'd clause.

反话 2024-10-24 22:57:22

我认为,对于你想做的事情,在表达式中设置 valrowcol 的值可能就足够了-组合时间,而不是让解释器获取 val 等的值。在评估时(在 Racket 中不可能)。

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

请注意,几乎可以肯定有一种更好、更简洁的方法可以在不使用 eval 的情况下完成您想要完成的任务,当然,除非您这样做是为了练习使用 eval

I think that, for what you want to do, it may be enough to set the values of val, row, and col at expression-composition time, rather than have the interpreter grab the values of val et al. at eval-time (impossible in Racket).

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

Note that there is almost certainly a nicer, cleaner way to accomplish what you are trying to do without eval, unless, of course, you are doing this to practice using eval.

风吹过旳痕迹 2024-10-24 22:57:22

一般来说,正如 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)

In general, there's no way to access the lexical environment with eval, as Eli points out. However, I believe that this workaround will do what you want:

#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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文