concurrency - Clojure - Map 和 Reduce 之间的区别 // 将一个转换为另一个
问题描述
(defn DoubleFrequency []
(def s (slurp "Example.txt"))
(def m (reduce #(assoc %1 %2 (inc (%1 %2 0)))
{}
(re-seq #".." s)))
(def c (count m))
(doseq [[k x] m]
(println k ":" (/ x c))))
我正在尝试将并发应用到我的程序中,并且我想使用 pmap,但我不确定如何将它应用到我当前的代码中。该功能对于单核是正确的,但理想情况下,我想以某种方式用 pmap 替换 reduce 并获得相同的结果。
解决方案
首先,您要编写的功能被称为frequencies
:
user> (frequencies [1 2 1 3 1 4 4])
;;=> {1 3, 2 1, 3 1, 4 2}
它确实是单线程的。所以让我们试着让它平行。
最初的方法reduce
是正确的方向,虽然它也不是并行的,但它可以用于与 clojure 的标准库并发设施,即reducers进行并行处理。
首先,让我们稍微重写一下你的 reducer 函数,做同样的事情,但以更惯用的方式(它是可选的,但有利于可读性):
#(assoc %1 %2 (inc (%1 %2 0)))
=>#(update %1 %2 (fnil inc 0))
然后我们可以使用以下方法进行并行减少fold
:
(require '[clojure.core.reducers :as r])
(defn pfreq [data]
(r/fold
(partial merge-with +)
(fn [acc k] (update acc k (fnil inc 0)))
data))
这个想法是它按块拆分您的集合(如果它足够长),然后将块的结果与merge-with
:
user> (pfreq [1 2 1 3 1 4 1 5 2])
;;=> {1 4, 2 2, 3 1, 4 1, 5 1}
另请注意,该系列应该是“可折叠的”。默认情况下,持久向量和映射是可折叠的,re-seq
结果不是,所以你应该先将它转换为 vector: (vec (re-seq #"..x" s))
,否则你不会得到任何并行化,回退到 plain reduce
。
您显然可以使用 pmap 来解决这个问题,使用相同的策略:split -> map -> combine:
(defn pfreq2 [chunk-size data]
(->> data
(partition-all chunk-size)
(pmap frequencies)
(apply merge-with +)))
但这不像reducers
管道那样灵活和强大。
推荐阅读
- android - Create unique notes for users
- database - Transaction Management By @@Transactional Annotation
- php - Get title Attribute of input variable using PHP
- sql - 如果列的值匹配,则删除与特定 ID 对应的所有行
- graphql - 传递参数子类型 graphql
- python - python中的SQlite用户定义函数
- javascript - 有没有办法沿 Three.js 中的路径设置 ExtrudeBufferGeometrie 的初始形状方向?
- gis - 使用 OpenLayers 5 在地图上添加旋转的卫星图像
- php - Slim-3 php 框架显示页面未找到错误,即使 url 正确?
- android - 自定义视图翻译动画在回收站视图中不起作用