我正在搞乱Clojure地图,我发现了这种我无法理解的情况。
假设我有一张这样的地图:
(def map-test {:name "head" :size 3})
我想更改此地图的值,在Clojure中,通常的方法是使用修改后的数据生成新的地图。
所以我有这个功能:
(defn map-change
[part]
{:name (str (:name part) "-" 1) :size (:size part)})
正如预期的那样,调用(map-change map-test)
会返回:{:name "head-1", :size 3}
所以我使用map
编写了这个函数来克隆哈希映射给定次数,比如{:name "head-1" ...}{:name "head-2" ...}{:name "head-3" ...}
等:
(defn repeat-test
[part times]
(map #({:name (str (:name part) "-" %) :size (:size part)}) (range 1 (inc times))))
但是当我拨打(repeat-test map-test 5)
时,我得到了一个我无法理解的例外:
Wrong number of args (0) passed to: PersistentArrayMap
调度器在评估:size
(:size part)=>3
时抛出此异常
这是stacktrace的最后一部分:
Unhandled clojure.lang.ArityException
Wrong number of args (0) passed to: PersistentArrayMap
AFn.java: 429 clojure.lang.AFn/throwArity
AFn.java: 28 clojure.lang.AFn/invoke
REPL: 80 clj-lab-00.hobbits/repeat-test/fn
core.clj: 2644 clojure.core/map/fn
LazySeq.java: 40 clojure.lang.LazySeq/sval
LazySeq.java: 49 clojure.lang.LazySeq/seq
RT.java: 521 clojure.lang.RT/seq
core.clj: 137 clojure.core/seq
core_print.clj: 46 clojure.core/print-sequential
core_print.clj: 153 clojure.core/fn
core_print.clj: 153 clojure.core/fn
MultiFn.java: 233 clojure.lang.MultiFn/invoke
core.clj: 3572 clojure.core/pr-on
core.clj: 3575 clojure.core/pr
core.clj: 3575 clojure.core/pr
AFn.java: 154 clojure.lang.AFn/applyToHelper
....
但是,如果我使用非匿名函数执行与匿名函数相同的操作:
(defn map-change
[part i]
{:name (str (:name part) "-" i) :size (:size part)})
(defn repeat-test
[part times]
(map #(map-change part %1) (range 1 (inc times))))
现在调用(repeat-test map-test 5)
有效。为什么?我错过了什么?
答案 0 :(得分:6)
错误类似于此简化示例:
(map #({:a %}) [1 2 3])
clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentArrayMap
您可以展开#({:a %})
以查看实际编译和执行的代码:
(macroexpand '#({:a %}))
;;=> (fn* [p1__21110#] ({:a p1__21110#}))
换句话说,#({:a %})
扩展为类似(fn [x] ({:a x}))
的内容。该函数体内的问题是映射被称为函数,没有参数。
地图的行为类似于功能:它们是键的功能。但他们期望至少有一个论点,最多两个:
({:a 1} :a) ;;=> :a
({:a 1} :b 2) ;;=> 2
您根本不打算将地图称为功能。你只是想拥有地图。匿名函数文字总是扩展为函数调用,不能产生直接值。您可以通过以下几种方式解决这个问题:
#(-> {:a %})
#(identity {:a %})
#(hash-map :a %)
#(do {:a %})
(fn [x] {:a x})
我更喜欢后者,虽然我发现第一个非常有趣。这就像是说:我想要归还我指向的东西。