变压器与减速器的区别是什么? - Clojure

时间:2018-03-05 17:18:01

标签: clojure transform reducers

从我收集的变换器是使用改变,改变元素集合的函数。就像我在

的集合中为每个元素添加了1
[1 2 3 4 5]

它变成了

[2 3 4 5 6]

但是为此编写代码看起来像

(map inc)

但是我一直把这种代码与减速器混淆了。因为它产生了新的累积结果。

我问的问题是,变压器和减速器有什么区别?

2 个答案:

答案 0 :(得分:1)

事实证明,集合的许多转换可以用reduce表示。例如,map可以实现为

(defn map [f coll] (reduce (fn [x y] (conj x (f y))) [] [0 1 2 3 4]))

然后你会打电话给

(map inc [1 2 3 4 5])

获取

[2 3 4 5 6]

在我们自制的map实现中,我们传递给reduce的函数是

(fn [x y] (conj x (f y))))

其中f是我们想要应用于每个元素的函数。所以我们可以编写一个为我们生成这样一个函数的函数,传递我们想要映射的函数。

(defn mapping-with-conj [f] (fn [x y] (conj x (f y))))

但是我们仍然看到上述函数中存在conj,假设我们想要向集合中添加元素。我们可以通过额外的间接获得更大的灵活性:

(defn mapping [f] (fn [step] (fn [x y] (step x (f y)))))

然后我们可以像这样使用它:

(def increase-by-1 (mapping inc))
(reduce (increase-by-1 conj) [] [1 2 3])

您引用的(map inc)执行我们对(mapping inc)的调用。你为什么要这样做呢?答案是它为我们提供了很多构建东西的灵活性。例如,我们可以做

而不是建立集合
(reduce ((map inc) +) 0 [1 2 3 4 5])

这将为我们提供映射集合的总和[2 3 4 5 6]。或者我们可以通过简单的函数组合添加额外的处理步骤。

(reduce ((comp (filter odd?) (map inc)) conj) [] [1 2 3 4 5])

将在我们映射之前首先从集合中删除偶数元素。 Clojure中的transduce函数基本上完成了上述内容的功能,但也处理了另外几个额外的细节。所以你实际上会写

(transduce (comp (filter odd?) (map inc)) conj [] [1 2 3 4 5])

总结一下,Clojure中的map函数有两个arities。像(map inc [1 2 3 4 5])一样调用它将映射集合的每个元素,以便获得[2 3 4 5 6]。像(map inc)一样调用它给我们的函数的行为与上面解释中的mapping函数非常相似。

答案 1 :(得分:0)

你可能只是混淆了各种命名法(正如上面的评论所暗示的那样),但我会通过解释你所说的减速器和变压器的意义来回答我认为你的问题。

<强>减少

减少函数(您可能认为是减速器)是一个获取累计值和当前值并返回新累计值的函数。

(accumulated, current) => accumulated

这些函数被传递给reduce,它们依次逐步执行一个序列,执行reduce函数的主体所说的两个参数(累积和当前),然后返回一个新的累积值,它将是用作下一次调用reduce函数的累加值(第一个参数)。

例如,可以将plus视为缩减功能。

(reduce + [0 1 2]) => 3

首先,使用0和1调用reduce函数(在本例中加),返回1.在下一次调用时,1现在是累计值,2是当前值,所以plus用1调用2,返回3,完成减少,因为集合中没有其他要处理的元素。

查看reduce实现的简化版本可能会有所帮助:

(defn reduce1
  ([f coll] ;; f is a reducing function
      (let [[x y & xs] coll]
           ;; called with the accumulated value so far "x"
           ;; and cur value in input sequence "y"
           (if y (reduce1 f (cons (f x y) xs)) 
               x)))
  ([f start coll]
      (reduce1 f (cons start coll))))

你可以看到功能&#34; f&#34; ,或&#34;减少功能&#34;在每次迭代时调用两个参数,到目前为止的累计值,以及输入序列中的下一个值。此函数的返回值用作下一个调用中的第一个参数,因此具有类型:

(x, y) => x

<强>转化

转换,我认为你的意思,表明输入的形状不会改变,但只是根据任意函数进行修改。这将是您传递给map的函数,因为它们应用于每个元素并构建一个具有相同形状的新集合,但该函数应用于每个元素。

(map inc [0 1 2]) => '(1 2 3)

注意形状是相同的,它仍然是一个3元素序列,而在上面的缩减中,你输入一个3元素序列并返回一个整数。减少可以改变最终结果的形状,地图不会。

请注意,我说&#34;形状&#34;不会发生变化,但每个元素的类型可能会根据您的变换而变化。功能确实:

(map #(list (inc %)) [0 1 2]) => '((1) (2) (3))

它仍然是一个3元素序列,但现在每个元素都是一个列表,而不是一个整数。

<强>附录:

在Clojure,Reducers和Transducers中有两个相关的概念,我只是想提一下,因为你问过减速器(在Clojure中具有特定含义)和变换器(这是Clojurists通常通过转换功能指定的名称)简写&#34; xf&#34;)。如果我试图在这里解释两者的细节,它会把这个已经很久的答案变成一个短篇小说,并且它比其他人做得更好:

传感器: http://elbenshira.com/blog/understanding-transducers/ https://www.youtube.com/watch?v=6mTbuzafcII

减速器和传感器: https://eli.thegreenplace.net/2017/reducers-transducers-and-coreasync-in-clojure/