Clojure中的变量作用域+eval



在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创建的名称空间。

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

谢谢!

1.:

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

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

和:

[…]

  1. 在当前命名空间中进行查找以查看是否存在映射从符号到变量。如果是value是的绑定值符号所指的var。

  2. 这是一个错误。

eval在空(CL语言中为null)词汇环境中评估形式。这意味着,您不能从调用方的作用域访问词法变量绑定。此外,binding为现有的变量创建了新的绑定,这就是为什么如果没有declared或defed作为您试图绑定的变量,您就不能"单独"使用它。此外,词法变量(至少在CL中,但如果Clojure不是这样的话,我会感到惊讶)在运行时已经不存在了——它们被转换为地址或值。

另请参阅我以前关于这个主题的帖子。

2.:

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

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

顺便说一句:我想你知道为什么你需要eval,当其他解决方案合适时,它的使用被认为是邪恶的。

最新更新