简体   繁体   English

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

[英]Exception using map and anonymous functions in Clojure

I was messing around with Clojure maps and I discovered this situation that I can't understand. 我在弄乱Clojure地图,发现这种情况我无法理解。

Let's say I got a map like this: 假设我有一个这样的地图:

(def map-test {:name "head" :size 3})

I want to change the value of this map, in Clojure the usual way is to generate a new one with the modified data. 我想更改此地图的值,在Clojure中,通常的方法是使用修改后的数据生成一个新地图。

So I have this function: 所以我有这个功能:

(defn map-change
  [part]
  {:name (str (:name part) "-" 1) :size (:size part)})

As expected the invocation (map-change map-test) returns: {:name "head-1", :size 3} 如预期的那样,调用(map-change map-test)返回: {:name "head-1", :size 3}

So I wrote this function using map to clone the hash-map a given number of times, like this {:name "head-1" ...}{:name "head-2" ...}{:name "head-3" ...} etc: 因此,我使用map编写了此函数,以将哈希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))))

But I got an exception that I cannot understand when I call (repeat-test map-test 5) : 但是我打电话时遇到了一个我无法理解的异常(repeat-test map-test 5)

Wrong number of args (0) passed to: PersistentArrayMap

The debugger throws this exception when is assigning the value to :size right after has evaluated (:size part)=>3 在评估(:size part)=>3之后立即将值分配给:size时,调试器将引发此异常。

Here's the last part of the 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
....

But if I use a non anonymous function that does the same operation as the anonymous one: 但是,如果我使用的非匿名函数执行的操作与匿名函数相同:

(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))))

Now calling (repeat-test map-test 5) works. 现在调用(repeat-test map-test 5) Why ? 为什么呢 What am I missing ? 我想念什么?

The error is similar to this simplified example: 该错误类似于以下简化示例:

(map #({:a %}) [1 2 3])

clojure.lang.ArityException: Wrong number of args (0) passed to: PersistentArrayMap

You can expand #({:a %}) to see what code is actually being compiled and executed: 您可以展开#({:a %})来查看实际正在编译和执行的代码:

(macroexpand '#({:a %}))
;;=> (fn* [p1__21110#] ({:a p1__21110#}))

In other words, #({:a %}) expands to something like (fn [x] ({:ax})) . 换句话说, #({:a %})扩展为类似(fn [x] ({:ax})) The problem in the body of this function is that the map is called as a function, with no arguments. 该函数主体中的问题在于,该映射被称为没有参数的函数。

Maps behave like functions: they are functions of their keys. 地图的行为类似于功能:它们是键的功能。 But they expect at least one argument and at most two: 但是他们希望至少有一个论点,最多有两个:

({:a 1} :a) ;;=> :a
({:a 1} :b 2) ;;=> 2

You didn't intend to call the map as a function at all. 您根本不打算将地图作为一个函数来调用。 You just wanted to have the map. 您只想拥有地图。 The anonymous function literal always expands into a function call and cannot yield a direct value. 匿名函数文字总是扩展为函数调用,并且不能产生直接值。 You can solve this several ways: 您可以通过以下几种方法解决此问题:

  • #(-> {:a %})
  • #(identity {:a %})
  • #(hash-map :a %)
  • #(do {:a %})
  • (fn [x] {:ax})

I prefer the latter, although I find the first one pretty funny. 我更喜欢后者,尽管我发现第一个很有趣。 It's like saying: I want to return the thing I'm pointing at. 就像在说:我想退回我所指的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM