在Clojure中使用map和匿名函数的异常

时间:2016-10-08 20:57:52

标签: dictionary exception clojure

我正在搞乱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)有效。为什么?我错过了什么?

1 个答案:

答案 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})

我更喜欢后者,虽然我发现第一个非常有趣。这就像是说:我想要归还我指向的东西。