with-redefs在地图中使用的函数

时间:2013-10-13 03:20:39

标签: clojure

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

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> 

谢谢!

1 个答案:

答案 0 :(得分:6)

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

然而,在bar中,vector不是map的参数,而是名称引用vector的匿名函数。结果是,虽然匿名函数仅被评估一次,但每次调用匿名函数时都将解析vector。因为map是懒惰的,所以在代码退出with-redefs之后就会发生这种情况(除了您的力量评估时)。

关键是在函数调用中 - 如(map vector (range 3)) - 每个参数都被评估,调用函数得到那些评估的结果。这意味着map中的foo调用会重新定义vector,而map中的bar调用会获得一个仍然需要查找的函数{{ 1}}在调用时按名称命名。

Clojure.org page on evaluation提供了有关如何将符号解析为对象的一些细节。这也是late binding的一个例子。