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、eval
、load-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
为现有变量创建新的绑定,这就是为什么您不能“单独”使用它,而没有 declare
d 或 def
ed 尝试绑定的变量。此外,词法变量(至少在 CL 中,但如果 Clojure 不是这种情况,我会感到惊讶)在运行时已经不复存在——它们被转换为地址或值。
有关此主题,另请参阅我的older post。
2.:
因此,您必须使用动态变量。你可以避免显式的def
,但你至少仍然需要declare
它们(其中def
s 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的主要内容,如果未能解决你的问题,请参考以下文章