4

我想知道下面的片段中发生了什么。为什么在不强制评估序列的情况下不能正确地重新定义函数?

user> (defn foo [] (map vector (range 3)))
#'user/foo
user> (defn bar [] (map #(vector %) (range 3)))
#'user/bar
user> (foo)
([0] [1] [2])
user> (bar)
([0] [1] [2])
user> (with-redefs [vector (fn [_] "what does the fox say?")] (foo))
("what does the fox say?" "what does the fox say?" "what does the fox say?")
user> (with-redefs [vector (fn [_] "what does the fox say?")] (bar))
([0] [1] [2])
user> (with-redefs [vector (fn [_] "what does the fox say?")] (vec (bar)))
["what does the fox say?" "what does the fox say?" "what does the fox say?"]
user> 

谢谢!

4

1 回答 1

6

不同之处在于,当您调用foo,vector作为 的参数时map,会评估一次(在这种情况下,这意味着将其解析为函数对象)并且不需要再次解析。即使在您的代码退出后,也会使用相同的函数对象with-redefs

但是,在bar中,这不是vector的参数map,而是vector按名称引用的匿名函数。结果是,虽然匿名函数只被评估一次,但vector每次调用匿名函数时都会被解析。因为map是懒惰的,这发生在代码已经退出之后with-redefs(强制评估时除外)。

关键是在函数调用中——比如(map vector (range 3))——每个参数都被评估,调用函数得到这些评估的结果。这意味着map调用 infoo获取 redefined vector,而map调用 inbar获取一个函数,该函数在调用时仍需要按vector名称查找。

Clojure.org的评估页面提供了有关符号如何解析为对象的一些详细信息。这也是后期绑定的一个例子。

于 2013-10-13T04:49:50.183 回答