如何使用0和2元格编写自定义归约函数

时间:2019-04-16 00:11:09

标签: clojure

clojure中的以下示例以各种方式调用+的无效和二进制情况:

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

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

我尝试按照this blog post (warning: no https)中所述用+替换mean-reducer

在没有参数的情况下,我更改了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

认为这意味着以某种方式将线交叉并且将(range 4)的元素绑定到memo,但是我不确定为什么会发生这种情况前提是带有明确的初始元素的案例成功了。

1 个答案:

答案 0 :(得分:3)

当您没有向reduce传递任何初始值时,将使用范围的前两个元素mean-reducer调用(mean-reducer 0 1)的2个版本。来自reduce个文档:

  

如果未提供val,则返回将f应用于coll的前2个项目,然后将f应用于该结果和第3个项目的结果,等等。

如果要使用reduce,则需要向mean-reducer提供初始值(并且不会使用其0值)。 reduce的2-arar降低功能有两个不同的“合约”,具体取决于是否指定了初始值。

clojure.core.reducers

clojure.core.reducers/reduce在没有提供初始值的情况下具有您想要的行为:

  

未提供init时,将使用(f)。

(r/reduce mean-reducer (range 4))
=> {:sum 6, :count 4}