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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了变压器与减速器的区别是什么? - Clojure相关的知识,希望对你有一定的参考价值。

从我收集的变换器是使用改变,改变元素集合的函数。就像我确实在一个集合中的每个元素中添加了1

[1 2 3 4 5]

它变成了

[2 3 4 5 6]

但为此编写代码看起来像

(map inc)

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

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

答案

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

减少:

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

(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))))

您可以看到函数“f”或“reduce函数”在每次迭代时都会调用两个参数,即到目前为止的累计值,以及输入序列中的下一个值。此函数的返回值用作下一个调用中的第一个参数,因此具有以下类型:

(x, y) => x

转化:

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

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

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

请注意,我说“形状”不会改变,但每个元素的类型可能会根据“转换”函数的作用而改变:

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

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

附录:

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

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

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

另一答案

事实证明,集合的许多转换可以用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实现中,我们传递的功能是减少的

(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函数非常相似。

以上是关于变压器与减速器的区别是什么? - Clojure的主要内容,如果未能解决你的问题,请参考以下文章

什么是Clojure的可折叠系列?

Laravel 变压器与资源

Clojure.logic 与 The Reasoned Schemer 的区别

Clojure 1.8的套接字repl和nREPL之间的具体区别是什么?

Scala,Groovy,Clojure三门语言的区别

什么是电源正激和反激? 正激和反激有什么区别特点?如何快速区分