Clojure: defn和fn有何不同?

7
如果我明确定义一个函数,就像这样:(defn f [x] (get x "a")), 那么(-> {"a" 1} f)(f {"a" 1})都能按预期工作。
然而,如果我使用匿名函数,只有(#(get % "a") {"a" 1})能正常工作,但(-> {"a" 1} #(get % "a"))会抛出异常:CompilerException java.lang.ClassCastException: clojure.lang.PersistentArrayMap cannot be cast to clojure.lang.ISeq, compiling:(NO_SOURCE_PATH:1:1)
1个回答

12

#(get % "a")会被阅读器展开:

 user=> '#(get % "a")
 (fn* [p1__852#] (get p1__852# "a"))

在这种情况下,您可以忽略fn和fn*之间的区别。

(-> ...)是一个宏,只是重新处理其参数:

user=> (macroexpand-1 '(-> {"a" 1} f))
(f {"a" 1})

请注意,只有在函数f周围没有括号时才会将括号包装到f周围,因此:
user=> (macroexpand-1 '(-> {"a" 1} (f)))
(f {"a" 1})

但是当应用于fn宏时,这种方法不会像您预期的那样起作用:

user=> (macroexpand-1 '(-> {"a" 1} (fn [x] (get x "a"))))
(fn {"a" 1} [x] (get x "a"))

或者在 #(...) 阅读器表单上:
user=> (macroexpand-1 '(-> {"a" 1} #(get % "a")))
(fn* {"a" 1} [p1__867#] (get p1__867# "a"))

通常的解决方法是将匿名函数放在列表中,但如果可以使用命名函数,我认为它会更易读:
user=> (macroexpand-1 '(-> {"a" 1} (#(get % "a"))))
((fn* [p1__870#] (get p1__870# "a")) {"a" 1})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接