Clojure中的变量范围+ eval

gil*_*esc 18 binding eval clojure let

在Clojure,

(def x 3)
(eval '(prn x))
Run Code Online (Sandbox Code Playgroud)

打印3,而

(let [y 3]
   (eval '(prn y)))
Run Code Online (Sandbox Code Playgroud)

(binding [z 3] (eval '(prn z)))
Run Code Online (Sandbox Code Playgroud)

生成'无法解析var'异常.

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

  1. 为什么第一个代码示例工作而不是后两个?
  2. 如何eval使用绑定变量的表单而不使用def

谢谢!

dan*_*lei 15

1:

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

It is an error if there is no global var named by the symbol […]
Run Code Online (Sandbox Code Playgroud)

和:

[...]

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

  2. 这是一个错误.

eval评估空(在CL-lingo中为null)词法环境中的表单.这意味着,您无法从调用者的作用域访问词法变量绑定.此外,binding创造了现有瓦尔新的绑定,这就是为什么你不能用它"本身",而无需declared或def编试图绑定变量.此外,词汇变量(至少在CL中,但如果Clojure不是这样,我会感到惊讶)已经在运行时停止存在 - 它们被转换为地址或值.

另见我关于此主题的旧帖子.

2:

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

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

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

  • 请注意,这在Clojure 1.3中不起作用.你必须使用`(声明^:动态x)`. (3认同)