Clojure:减速器的精确定义

Clojure:减速器的精确定义,clojure,reducers,Clojure,Reducers,定义了一个减速器,如下所示: 还原器是可还原集合(知道如何还原自身的集合)与还原函数(还原过程中需要执行的操作的“配方”)的组合 以下是reducer函数的实现(来自Rich) 似乎更准确的说法是,还原函数是一个可还原集合和一个还原函数转换器(后来称为a)的组合,而不是一个还原函数 减速器不“知道”任何有关减速功能的信息,该功能由reduce提供。它所知道的只是一个“配方”,用于获取一些还原功能并对其进行修改(转换) 我对“减速器”的理解正确吗?或者,关于“具有还原功能的可还原集合”的定义,我有

定义了一个减速器,如下所示:

还原器是可还原集合(知道如何还原自身的集合)与还原函数(还原过程中需要执行的操作的“配方”)的组合

以下是
reducer
函数的实现(来自Rich)

似乎更准确的说法是,还原函数是一个可还原集合和一个还原函数转换器(后来称为a)的组合,而不是一个还原函数

减速器不“知道”任何有关减速功能的信息,该功能由
reduce
提供。它所知道的只是一个“配方”,用于获取一些还原功能并对其进行修改(转换)


我对“减速器”的理解正确吗?或者,关于“具有还原功能的可还原集合”的定义,我有什么遗漏吗?

文档中的语言混淆了还原器和转换器。传感器可以被看作是减速器的优化实现,其目标是减少对象分配(以及后续GC)。此优化不会更改减速器的概念模型

所以,坚持使用普通的老
reduce
,其目的只是提供一种从序列中累积结果的方法。最简单的示例是对序列求和:

(ns clj.core
  (:require [clojure.string :as str] )
  (:use tupelo.core)) ; it->

(def values (range 6))
(spyx values)

(def total (reduce + 0 values))
(spyx total)

;=> values => (0 1 2 3 4 5)
;=> total => 15
(def integral (reduce (fn [cum-state new-val]             ; accumulating function
                        (let [integ-val (+ (:running-total cum-state)  new-val) ]
                          { :integ-vals     (conj (:integ-vals cum-state) integ-val)
                            :running-total  integ-val} ))
                      {:integ-vals [] :running-total 0}   ; initial value
                      values))                            ; sequence to process
(spyx integral)
;=> integral => {:integ-vals [0 1 3 6 10 15], :running-total 15}
然而,“还原功能”可以做任何事情。它还可以返回序列,而不仅仅是标量值:

(def duplicate (reduce (fn [cum-result new-val]         ; accumulating function
                        (conj cum-result new-val))
                      []                                ; initial value
                      values))                          ; sequence to process
(spyx duplicate)
;=> duplicate => [0 1 2 3 4 5]
下面是一个更复杂的约化函数,用于计算输入序列的积分:

(ns clj.core
  (:require [clojure.string :as str] )
  (:use tupelo.core)) ; it->

(def values (range 6))
(spyx values)

(def total (reduce + 0 values))
(spyx total)

;=> values => (0 1 2 3 4 5)
;=> total => 15
(def integral (reduce (fn [cum-state new-val]             ; accumulating function
                        (let [integ-val (+ (:running-total cum-state)  new-val) ]
                          { :integ-vals     (conj (:integ-vals cum-state) integ-val)
                            :running-total  integ-val} ))
                      {:integ-vals [] :running-total 0}   ; initial value
                      values))                            ; sequence to process
(spyx integral)
;=> integral => {:integ-vals [0 1 3 6 10 15], :running-total 15}
因此,这是与
map
相比的最大区别。我们将
map
称为:

(def y (map f x))
其中,
x
y
是序列,结果如下

y(0) = f( x(0) )   ; math notation used here
y(1) = f( x(1) )
y(2) = f( x(2) )
...
y(0) = f( init, x(0) )   ; math notation used here
y(1) = f( y(0), x(1) )
y(2) = f( y(1), x(2) )
...
因此,每个y(i)仅取决于函数
f
和x(i)。相反,我们将
reduce
定义为:

(def y (map f x))
(定义y(减少f初始x))

其中,
x
y
是序列,
init
是标量(如
0
[]
)。结果看起来像

y(0) = f( x(0) )   ; math notation used here
y(1) = f( x(1) )
y(2) = f( x(2) )
...
y(0) = f( init, x(0) )   ; math notation used here
y(1) = f( y(0), x(1) )
y(2) = f( y(1), x(2) )
...

因此,还原函数
f
是两个值的函数:累积结果和新的
x
值。

当您从博客查看
还原函数的源代码时:

(defn reducer
  ([coll xf]
   (reify
    clojure.core.protocols/CollReduce
    (coll-reduce [_ f1 init]
      (clojure.core.protocols/coll-reduce coll (xf f1) init)))))
您可以看到,reducer要求集合进行自我缩减(通过调用coll上的
coll reduce
),并提供一个缩减函数,该函数是通过调用缩减函数转换器(
(xf f1)
)生成的

所以我想说这句话:

还原器是可还原集合
和还原功能变压器
(后称为传感器)的组合,而不是还原功能


更准确,因为您首先需要的是一个可还原的集合一个还原函数转换器。还原功能只是调用还原功能转换器的结果。

“可以将传感器视为还原器的优化实现”…嗯?传感器应将一个减速器“转换”为另一个减速器。它是从约化子空间到约化子空间的映射。它本身并不是一个减速机。“传感器直接合成,不知道输入或创建中间聚合。”不创建中间序列是这里的好处,因此与一次一个地应用多个序列操作(映射、过滤、减少…)相比,内存波动更少。