【问题标题】:Which is better?: (reduce + ...) or (apply + ...)?哪个更好?:(reduce + ...)还是(apply + ...)?
【发布时间】:2010-11-16 04:37:31
【问题描述】:

我应该使用

(apply + (filter prime? (range 1 20)))

(reduce + (filter prime? (range 1 20)))

编辑:这是优化工具包中 clojure 中素数的来源。

(defn prime?  [n]  
  (cond
    (or (= n 2) (= n 3))          true
    (or (divisible? n 2) (< n 2)) false
    :else                        
     (let [sqrt-n (Math/sqrt n)]
       (loop [i 3]
           (cond
              (divisible? n i) false
              (< sqrt-n i)     true
              :else            (recur (+ i 2)))))))

【问题讨论】:

  • Clojure: reduce vs. apply 的可能副本。链接的问题比这个问题要新,但它有 IMO 更好的答案,所以我提名它为幸存者。

标签: lisp clojure scheme


【解决方案1】:

如果你问的是性能,reduce 会好一点:

(time (dotimes [_ 1e6] (apply + (filter even? (range 1 20)))))
"Elapsed time: 9059.251 msecs"
nil

(time (dotimes [_ 1e6] (reduce + (filter even? (range 1 20)))))
"Elapsed time: 8420.323 msecs"
nil

在这种情况下大约有 7% 的差异,但 YMMV 取决于机器。

您尚未提供prime? 函数的来源,因此我将even? 替换为谓词。请记住,您的运行时可能由 prime? 主导,在这种情况下,reduceapply 之间的选择就更不重要了。

如果您要问哪个更“lispy”,那么我会说reduce 实现更可取,因为您正在做的是函数式编程意义上的reduce/fold。

【讨论】:

    【解决方案2】:

    我认为reduce 可用时会更可取,因为apply 使用列表作为函数的参数,但是当列表中有大量(例如,一百万)元素时,您将构造一个带有一百万个参数的函数调用!这可能会导致一些 Lisp 实现出现问题。

    【讨论】:

    • Common Lisp 有一个常量 CALL-ARGUMENTS-LIMIT。
    • 好点,虽然 Clojure 没有问题 - Clojure 乐于以这种方式构建任意长的参数列表(即使是无限的懒惰的...)
    【解决方案3】:

    (reduce op ...) 是标准,(apply op ...) 是例外(尤其是 str 和 concat)。

    【讨论】:

      【解决方案4】:

      我希望 apply 能够实现一个可能很难看的惰性列表,并且您永远不想假设您的列表不是惰性的,因为您可能会突然发现自己被大量内存使用所困扰。

      Reduce 将一个接一个地抓取它们并将结果汇​​总成一个整体,而不是一次包含整个列表。

      【讨论】:

      • 在 Clojure 中应用实际上并没有实现完整的参数列表,除非它需要。 (apply (fn [& args] (take 5 args)) (range)) 例如工作正常。
      【解决方案5】:

      我将扮演魔鬼的拥护者并为apply辩护。

      reduce 是 clojure 对 fold(更确切地说是foldl)的左折叠,通常用初始元素定义,因为折叠操作有两个部分:

      • 初始(或“零”)值

      • 组合两个值的操作

      所以要找到一组数字的总和,使用+ 的自然方式是(fold + 0 values),或者在clojure 中,(reduce + 0 values)

      这显式显示了空列表的结果,这很重要,因为在这种情况下+ 返回0 对我来说并不明显——毕竟+ 是一个二元运算符 em>(fold 需要或假设的所有内容)。

      现在,实际上,clojure 的+ 被定义为更多,而不是二元运算符。它将需要许多甚至零值。凉爽的。但如果我们使用这个“额外”信息,向读者发出信号是很友好的。 (apply + values) 这样做 - 它说“我以一种奇怪的方式使用 +,不仅仅是一个二元运算符”。这有助于人们(至少是我)理解代码。

      [问为什么apply 感觉更清晰很有趣。我认为部分原因是您对读者说:“看,+ 旨在接受多个值(这就是 apply 的用途),因此语言实现将包括零值的情况。” reduce 应用于单个列表时,该隐式参数不存在。]

      或者,(reduce + 0 values) 也可以。但是(reduce + values) 引发了我的本能反应:“嗯,+ 提供零吗?”。

      如果您不同意,那么请在您投反对票或发表回复之前,确定(reduce * values) 将返回一个空列表吗?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-11-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-30
        • 2021-06-09
        相关资源
        最近更新 更多