Clojure 中的变量作用域 + eval

Posted

技术标签:

【中文标题】Clojure 中的变量作用域 + eval【英文标题】:Variable scope + eval in Clojure 【发布时间】:2011-09-07 11:32:39 【问题描述】:

在 Clojure 中,

(def x 3)
(eval '(prn x))

打印 3,而

(let [y 3]
   (eval '(prn y)))

(binding [z 3] (eval '(prn z)))

生成“无法解析 var”异常。

根据http://clojure.org/evaluation、evalload-string等生成临时命名空间来评估其内容。因此,我希望上述代码示例都不起作用,因为 (def x 3) 是在我当前的命名空间中完成的,而不是 eval 创建的命名空间。

    为什么第一个代码示例有效,而后两个无效? 如何在不使用def 的情况下eval 绑定变量的表单?

谢谢!

【问题讨论】:

【参考方案1】:

1.:

这不起作用的原因(或多或少)在您链接的页面上给出:

It is an error if there is no global var named by the symbol […]

还有:

[…]

    在当前命名空间中进行查找以查看是否存在映射 从符号到变量。如果是这样,则 value 是绑定的值 符号引用的变量。

    这是一个错误。

eval 在空(CL-lingo 中的null)词法环境中计算表单。这意味着,您不能从调用者的范围访问词法变量绑定。此外,binding 为现有变量创建新的绑定,这就是为什么您不能“单独”使用它,而没有 declared 或 defed 尝试绑定的变量。此外,词法变量(至少在 CL 中,但如果 Clojure 不是这种情况,我会感到惊讶)在运行时已经不复存在——它们被转换为地址或值。

有关此主题,另请参阅我的older post。

2.:

因此,您必须使用动态变量。你可以避免显式的def,但你至少仍然需要declare它们(其中defs var 名称没有绑定):

user=> (declare ^:dynamic x)
#'user/x
user=> (binding [x 10] (eval '(prn x)))
10
nil

顺便说一句:我想您知道为什么需要 eval,并且当其他解决方案适合时,它的用途是 considered evil。

【讨论】:

请注意,这在 Clojure 1.3 中不起作用。你必须使用(declare ^:dynamic x) 谢谢!我现在明白了我的问题——我假设“null lexical scope”也意味着“null namespace”,但快速测试表明eval 在当前命名空间中工作,这就是它可以访问命名空间变量但不能访问的原因词法变量。非常好的答案,您的链接也很有帮助! 不客气!我很高兴它有所帮助。另外,感谢您的接受。 我猜你应该引用内部(prn x)。 我在尝试编写一个可以自动执行 (let [x 41, y (inc x)] :x x, :y y) 之类的宏时遇到了这个问题(显然只有大的 let 块和大量耐嚼的数学才有趣)。我尝试了(defmacro hashup [vars] ``(apply hash-map (map vector (map keyword ~vars) (map eval ~vars))))(只有一个反引号)并在这里遇到了问题。我没有办法干掉 let 块:(

以上是关于Clojure 中的变量作用域 + eval的主要内容,如果未能解决你的问题,请参考以下文章

利用eval()来“欺骗”JavaScript词法作用域

js中的eval方法详解–eval方法中的高级应用

eval全局作用域和局部作用域的坑!

作用域变量的实现如何在 *ngFor 中工作

js中的变量提升和函数提升

控制器controller与指令中的linkcontroller中同名变量作用域的关系