如何使用 "mean" 作为传感器中的最终归约函数?

How do I use "mean" as the final reducing function in a transducer?

我正在尝试估计单位正方形中所有点对的平均距离。 该传感器 returns 随机选择的 x 对点的距离矢量,但最后一步是取该矢量中所有值的平均值。有没有办法使用 mean 作为最终的归约函数(或将其包含在合成中)?

(defn square [x] (* x x))
(defn mean [x] (/ (reduce + x) (count x)))

(defn xform [iterations]
  (comp
   (partition-all 4) 
   (map #(Math/sqrt (+ (square (- (first %) (nth % 1)))
                       (square (- (nth % 2) (nth % 3))))))
   (take iterations)))

(transduce (xform 5) conj (repeatedly #(rand)))

[0.5544757422041136
 0.4170515673848907
 0.7457675423415904
 0.5560901974277822
 0.6053573945754688]


(transduce (xform 5) mean (repeatedly #(rand)))
Execution error (ArityException) at test.core/eval19667 (form-init9118116578029918666.clj:562).
Wrong number of args (0) passed to: test.core/mean

来自 transduce 的文档:

If init is not supplied, (f) will be called to produce it. f should be a reducing step function that accepts both 1 and 2 arguments, if it accepts only 2 you can add the arity-1 with 'completing'.

剖析这个:

  1. 你的函数需要 0-arity 来产生一个初始值——所以 conj 很好(它产生一个空向量)。
  2. 您需要提供一个 2 元函数来进行实际的归约 -- 同样 conj 在这里没问题
  3. 您需要提供 1-arity 函数来完成 - 这里是您想要的 你的 mean.

因此,正如文档所建议的那样,您可以使用 completing 来提供:

(transduce (xform 5) (completing conj mean) (repeatedly #(rand)))
; → 0.4723186070904141

如果您查看 completing 的源代码,您将了解它是如何生成的 所有这些:

(defn completing
  "Takes a reducing function f of 2 args and returns a fn suitable for
  transduce by adding an arity-1 signature that calls cf (default -
  identity) on the result argument."
  {:added "1.7"}
  ([f] (completing f identity))
  ([f cf]
     (fn
       ([] (f))
       ([x] (cf x))
       ([x y] (f x y)))))

如果您以不同方式实现 mean 函数,则无需在计算平均值之前收集所有值。这是基于 this Java code:

的实现方法
(defn mean
  ([] [0 1]) ;; <-- Construct an empty accumulator
  ([[mu n]] mu) ;; <-- Get the mean (final step)
  ([[mu n] x] ;; <-- Accumulate a value to the mean
   [(+ mu (/ (- x mu) n)) (inc n)]))

然后你这样使用它:

(transduce identity mean [1 2 3 4])
;; => 5/2

或者像这样:

(transduce (xform 5) mean (repeatedly #(rand)))
;; => 0.582883812837961