采用以下三个函数,分别在Haskell和Clojure中实现:
f :: [Int] -> Int
f = foldl1 (+) . map (*7) . filter even
(defn f [coll]
((comp
(partial reduce +)
(partial map #(* 7 %)
(partial filter even?)) coll))
(defn f [coll]
(transduce
(comp
(filter even?)
(map #(* 7 %)))
+ coll))
当它们应用于[1, 2, 3, 4, 5]
这样的列表时,它们都会返回42
。我知道前两个后面的机器是相似的,因为map
在Clojure中很懒,但第三个使用了传感器。有人可以展示执行这些功能的中间步骤吗?
答案 0 :(得分:4)
第二个和第三个示例之间的中间步骤与此特定示例相同 。这是因为 map 和 filter 是将序列延迟转换为序列的事实,因为您无疑已经知道了。
映射和滤波器的传感器版本使用与非传感器版本相同的基本功能定义,除了它们“汇合”(或不是,在滤波器的情况下)的方式结果流在其他地方定义。实际上,如果您查看地图的来源,则使用explicit data-structure construction functions,而transducer variant不使用此类函数 - 它们通过{{1}传入在非传感器版本中明确使用rf
意味着他们总是要处理序列
IMO,使用传感器的主要好处是,您可以定义进程,而您正在使用您的进程。因此,对您的第三个示例进行更有趣的重写可能是:
cons
它是应用程序作者的一项练习,决定何时需要这种抽象,但它绝对可以为重用提供机会。
您可能只需重写
(def process (comp (filter even)
(map #(* 7 %))))
(defn f [coll] (transduce process + collection))
并获得相同的效果;这是真的。 如果您的输入始终是一个序列(或者总是使用相同类型的流/您知道它将是什么类型的流),那么可以说创建一个传感器并不是一个好理由。但是这里可以证明重用的力量(假设过程是一个传感器)
(defn process [coll]
((comp
(partial map #(* 7 %)
(partial filter even?)) coll))
(reduce + (process coll))
传感器背后的动机主要是停止为不同的集合类型编写新的集合函数。 Rich Hickey提到他的写作功能,比如map<映射>会员名:mapcat< mapcat>等等在核心异步库 - 什么 map和mapcat 中已经定义了,但因为他们假设他们是对序列进行操作(我在上面链接的显式(chan 1 process) ;; an async channel which runs process on all inputs
(into [] process coll) ;; writing to a vector
(transduce + process coll) ;; your goal
),它们不能应用于异步通道。但是频道可以在传感器版本中提供自己的cons
,让他们重用这些功能。