2016-10-08 4 views
1

Я возился с картами 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) работ. Зачем ? Что мне не хватает?

ответ

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

Я предпочитаю последний, хотя я считаю, первый довольно забавный. Это как сказать: я хочу вернуть то, на что я указываю.

+1

Я бы добавил '# (do {: a%})' в качестве опции. – OlegTheCat

+1

Итак, правило для новичков - использовать '(fn [x] ...)' и избегать формы '#(), по крайней мере, до тех пор, пока я не пойму макросы лучше. – Marcs

+0

@OlegTheCat Это тоже возможность. Я добавил. Есть еще много опций: '# (и {: a%})', '# (или {: a%})' :-). @Marcs Я думаю, что это хорошее эмпирическое правило. Еще одно замечание: анонимные литералы функций не могут быть вложенными. –

Смежные вопросы