Clojure:defn 与 fn 有何不同?

标签 clojure language-lawyer

如果我明确定义这样的函数 (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)

最佳答案

#(get % "a")由读者展开:

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

在这种情况下,您可以忽略 fn 和 fn* 之间的区别。
(-> ...)是一个只重新线程化其参数的宏:
user=> (macroexpand-1 '(-> {"a" 1} f))
(f {"a" 1})

请注意,如果还没有括号,它只会将括号括在 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})

关于Clojure:defn 与 fn 有何不同?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16956767/

相关文章:

optimization - 计算对在 Clojure 1.4 中出现频率的快速方法

c++ - 为什么 "Guaranteed Copy Elision"不表示 push_back({arg1, arg2}) 与 emplace_back(arg1, arg2) 相同?

c++ - 模棱两可的注入(inject)类名不是错误

c++ - C++ 语法演化是如何管理的?

c++ - 使用整数指针将左值转换为右值

visual-c++ - 自定义分配器与 promise 和打包任务

clojure - 在 Clojure 中使用 slimv

macros - Clojure 中尚未提供的简单而引人注目的宏示例

jdbc - clojure jdbc : how to insert into a table with a specific schema?

clojure - 如何将两个参数的函数应用于序列?