【问题标题】:Variable scope + eval in ClojureClojure 中的变量作用域 + eval
【发布时间】: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/evaluationevalload-string等生成临时命名空间来评估其内容。因此,我希望上述代码示例都不起作用,因为 (def x 3) 是在我当前的命名空间中完成的,而不是 eval 创建的命名空间。

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

谢谢!

【问题讨论】:

    标签: binding clojure eval let


    【解决方案1】:

    1.:

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

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

    还有:

    […]

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

    2. 这是一个错误。

    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 块:(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多