Как написать пользовательскую функцию редукции в 0 и 2-х случаях - PullRequest
0 голосов
/ 16 апреля 2019

Следующий пример в clojure вызывает нулевой и двоичный регистры + различными способами:

(println 101 (+)) ; fine
(println 102 (+ (+) 4)) ; fine

(println 103 (reduce + (+) (range 4))) ; fine
(println 104 (reduce + (range 4))) ; fine

Я попытался заменить + на mean-reducer, как описано в thisсообщение в блоге (предупреждение: нет https) .

Я изменил функцию mean-reducer, чтобы явно показывать ее элемент идентификации {:sum 0 :count 0} при вызове без аргументов.

Это прекрасно работает дляпростые случаи приводят к (reduce mean-reducer (range 4)), но падают для самого (reduce mean-reducer (range 4)).

(defn mean-reducer
  ([] {:sum 0 :count 0})
  ([memo x]
         {
             :sum (+ x (memo :sum))
             :count (inc (memo :count))
         }))

(println 201 (mean-reducer)) ; fine
(println 202 (mean-reducer (mean-reducer) 4)) ; fine

(println 203 (reduce mean-reducer (mean-reducer) (range 4))) ; fine
;; (println 204 (reduce mean-reducer (range 4))) ; bad

запускается и выдает это с последней прокомментированной строкой.

% clojure mean_reducer.clj
201 {:sum 0, :count 0}
202 {:sum 4, :count 1}
203 {:sum 6, :count 4}

Сообщение об ошибке и трассировка стека, связанная с неудачным вызовом (reduce mean-reducer (range 4)), выглядит следующим образом:

(~/clojure/mean_reducer.clj:12:62).
    at clojure.lang.Compiler.load(Compiler.java:7647)
    at clojure.lang.Compiler.loadFile(Compiler.java:7573)
    at clojure.main$load_script.invokeStatic(main.clj:452)
    at clojure.main$script_opt.invokeStatic(main.clj:512)
    at clojure.main$script_opt.invoke(main.clj:507)
    at clojure.main$main.invokeStatic(main.clj:598)
    at clojure.main$main.doInvoke(main.clj:561)
    at clojure.lang.RestFn.applyTo(RestFn.java:137)
    at clojure.lang.Var.applyTo(Var.java:705)
    at clojure.main.main(main.java:37)
Caused by: java.lang.ClassCastException: class java.lang.Long cannot be cast to class clojure.lang.IFn (java.lang.Long is in module java.base of loader 'bootstrap'; clojure.lang.IFn is in unnamed module of loader 'app')
    at user$mean_reducer.invokeStatic(mean_reducer.clj:5)
    at user$mean_reducer.invoke(mean_reducer.clj:1)
    at clojure.lang.LongRange.reduce(LongRange.java:222)
    at clojure.core$reduce.invokeStatic(core.clj:6823)
    at clojure.core$reduce.invoke(core.clj:6810)
    at user$eval143.invokeStatic(mean_reducer.clj:13)
    at user$eval143.invoke(mean_reducer.clj:13)
    at clojure.lang.Compiler.eval(Compiler.java:7176)
    at clojure.lang.Compiler.load(Compiler.java:7635)
    ... 9 more

I думаю это означает, что каким-то образом линии пересекаются иэлемент (range 4) привязан к memo, но я не уверен, почему это произойдет, если случай с явным начальным элементом будет успешным.

1 Ответ

3 голосов
/ 16 апреля 2019

Когда вы не передаете начальное значение в reduce, 2-арная версия mean-reducer вызывается с первыми двумя элементами диапазона, т.е. (mean-reducer 0 1). От reduce документов:

Если val не указан, возвращает результат применения f к первым двум элементам в coll, затем применение f к этому результату и третьему элементу и т. Д.

Вам нужно будет ввести начальное значение в reduce, если вы хотите использовать mean-reducer (и его 0-арность не будет использоваться). reduce имеет два разных «контракта» для своей функции сокращения двух арностей в зависимости от того, указано ли начальное значение.

clojure.core.reducers

clojure.core.reducers/reduce работает именно так, как вы хотите, если не указано начальное значение:

Если init не указан, используется (f).

(r/reduce mean-reducer (range 4))
=> {:sum 6, :count 4}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...