我想知道下面的代码段中发生了什么。为什么在不强制评估序列的情况下不正确地重新定义函数?
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>
谢谢!
答案 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的一个例子。