【问题标题】:When To Use reduce Or Instead Use pmap何时使用 reduce 或改为使用 pmap
【发布时间】:2012-04-07 03:11:22
【问题描述】:

编辑:

数据确实是这样的。

1,000-00-000,GRABBUS,OCTOPUS,,M,26-Nev-12,,05 FRENCH TOAST ROAD,,VACANT,ZA,1867,(001) 111-1011,(002) 111-1000,,

我必须让它看起来很傻,因为它包含专有信息。

这是使用 clojure-csv 创建向量向量之前的样子。

我使用了解析后的数字来简化它,但它们并没有被简化为一个值。我想从 clojure-csv 解析数据中挑选某些列并创建一个较小的 csv 行。

如有任何困惑,请接受我的歉意。

结束编辑:

您如何确定何时使用 reduce 或使用 pmap?

不久前,我在博客上收到了关于 reduce 的评论。具体来说,评论说 reduce 一般不能并行化,但 map (pmap) 可以。

何时使用或不使用 reduce 会有所不同,例如 下面,有什么不同吗?

谢谢。

(def csv-row [1 2 3 4 5 6 7 8 9])
(def col-nums [0 1 4])

(defn reduce-csv-rowX
    "Accepts a csv-row and a list of columns to extract, and
     reduces the csv-row to the selected list using a list comprehension."
    [csv-row col-nums]
        (for [col-num col-nums
            :let [part-row (nth csv-row col-num nil)]]
            part-row))

(defn reduce-csv-row
    "Accepts a csv-row and a list of columns to extract, and
     reduces the csv-row to the selected list."
    [csv-row col-nums]
    (reduce
        (fn [out-csv-row col-num]
            (let [out-val (nth csv-row col-num nil)]
                (if-not (nil? out-val)
                    (conj out-csv-row out-val))))
        []
        col-nums))

编辑:

(defn reduce-csv-row “接受 csv 行和要提取的列列表,以及 将 csv 行减少到选定列表。” [csv-row col-nums] (减少 (fn [out-csv-row col-num] (让 [out-val (nth csv-row col-num nil)] (conj out-csv-row out-val))) [] 列号))

【问题讨论】:

    标签: clojure reduce pmap


    【解决方案1】:

    您也可以使用选择键,它以稍微不同的格式返回响应:

    (select-keys [1 2 3 4 5 6 7 8 9] [0 1 4])
    ;==> {4 5, 1 2, 0 1}
    

    即从键到值的映射。它在地图上看起来更好,但也适用于其他序列。

    您还可以查看 clojure.set/project,它类似于 select-keys(实际上在内部使用它),但用于整个表,而不是仅一行。

    【讨论】:

      【解决方案2】:

      Resuce 和 map 是一个很好的搭配,它们经常一起使用,以至于 map-reduce 现在是一个常见的行业术语。一般来说,map 用于将数据转换为可聚合的形式,reduce 将数据聚合为单个答案。 如果归约函数是可交换的,则归约可以并行化。例如,+ 的并行归约很好,/ 的效果较差。

      • 如果您想生成一个集合(如列表或向量),请使用map
      • 如果您想生成单个值,如 42,请使用 reduce

      【讨论】:

      • map 在我的情况下不起作用,我已经看到了可以很好地使用 reduce 来生成缩减向量的实例,而不仅仅是单个值。
      • 我也发现自己经常减少到一个集合
      【解决方案3】:

      通常,您希望使用可让您编写最简单代码的函数。这通常意味着可能的最具体的功能。在这种情况下,您可以将您的操作视为将列号列表转换为列中行的值列表。这对应于map,因此您可能想要使用map。你可以用reduce来写,但在这种情况下,你在调用reduce时重新实现了map,所以这可能是错误的方法。

      但是,有时reduce 是正确的选择。如果您试图将列表“减少”为任意值,map 对您毫无帮助。在这种情况下,reduce 就是您想要的,并且由于您的操作不可并行化,reduce 也不可并行化。

      如果您对您的reduce 代码不理想的原因进一步感兴趣,如果我们抽象出特定于应用程序的代码,我们会得到

      (reduce
       (fn [out-list current-val]
         (let [out-val (f current-val)]
           (if-not (nil? out-val)
             (conj out-list out-val))))
       []
       col-nums)
      

      一个复杂的因素是if-not 调用。目前,它的错误 - 如果 out-val 永远为零,您将丢弃到目前为止找到的所有内容并重新开始((if-not (nil? out-val) (conj out-list out-val)) 的返回是 nil,而 out-val 是 nil,所以 @ 987654337@ 将用作下一个out-list)。由于您的其他实现没有任何 nil 检查并且这个 nil 检查是错误的(因此可能从未使用过),我假设它可以被忽略。此时,您的代码是

      (reduce
       (fn [out-list current-val]
         (let [out-val (f current-val)]
           (conj out-list out-val)))
       []
       col-nums)
      

      这是map 的完全有效(尽管不是惰性)实现。使用对map 的实际调用,您可以消除所有与您的特定问题实际上无关的代码,而是专注于您实际尝试做的事情。通过查看 ivant 的解决方案,您可以看到它的效果。

      【讨论】:

      • 感谢您发现错误。我不需要测试 nil,只需 conj 结果。
      【解决方案4】:

      使用地图的解决方案可能如下所示:

      (defn reduce-csv-rowM [csv-row col-nums]
        (pmap (fn [pos] (nth csv-row pos)) col-nums))
      

      它很容易并行化,如果 csv-row 是一个向量 nth 是相当快的,所以没关系。

      所以在你的情况下,我认为地图解决方案是最好的,因为它比其他两个更容易理解,而且速度也更快。

      在一般情况下,map 和 reduce 是不可互换的,事实上它们一起非常有用(就像在 google 的 map-reduce 技术中一样)。

      【讨论】:

      • 此解决方案因 (IndexOutOfBoundsException clojure.lang.PersistentVector.arrayFor (PersistentVector.java:106) 而失败,但会打印我的测试文件中三行中的选定列。
      • @octopusgrabbus,你检查过第二个参数的索引是否正确吗?
      猜你喜欢
      • 1970-01-01
      • 2014-08-29
      • 1970-01-01
      • 1970-01-01
      • 2019-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-19
      相关资源
      最近更新 更多