哪个更好?:(减少+ ...)或(应用+ ...)?

unj*_*nj2 13 lisp scheme clojure

我应该用吗?

(apply + (filter prime? (range 1 20)))
Run Code Online (Sandbox Code Playgroud)

要么

(reduce + (filter prime? (range 1 20)))
Run Code Online (Sandbox Code Playgroud)

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

(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)))))))
Run Code Online (Sandbox Code Playgroud)

ala*_*ode 18

如果你在性能方面提出要求,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
Run Code Online (Sandbox Code Playgroud)

在这种情况下差异约为7%,但YMMV取决于机器.

您尚未提供prime?函数的源代码,因此我已将其替换even?为谓词.请记住,你的运行可由为主prime?,在这种情况下之间的选择reduceapply问题就更少了.

如果你问哪个更"lispy",那么我会说reduce实现是可取的,因为你正在做的是在函数编程意义上的减少/折叠.


new*_*cct 13

我认为reduce当它可用时会更好,因为apply使用列表作为函数的参数,但是当你在列表中有一个大数字 - 比如一百万个元素时,你将构造一个函数调用百万论点!这可能会导致一些Lisp实现出现问题.

  • Common Lisp具有恒定的CALL-ARGUMENTS-LIMIT. (6认同)
  • 好的一点虽然不是Clojure的问题 - Clojure很高兴以这种方式构建任意长的参数列表(甚至无限懒惰的事件......) (2认同)

cgr*_*and 8

(减少op ...)是规范和(应用op ...)异常(特别是对于str和concat).


Run*_*ult 7

我希望申请实现一个可能很丑的懒惰列表,你永远不想假设你的列表不是懒惰,因为你可能会突然发现自己被大量的内存使用所打扰.

Reduce将逐个抓住它们并将结果整合到一个整体中,从不将整个列表一次性整理.

  • 除非需要,否则在Clojure中应用实际上并没有实现完整的参数列表.(apply(fn [&args](取5 args))(range))例如可以正常工作. (2认同)

and*_*oke 6

我将扮演魔鬼的拥护者并争辩apply.

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

  • 初始(或"零")值

  • 用于组合两个值的操作

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

这显然显示了一个空列表的结果,这很重要,因为在这种情况下+返回我并不明显0- 毕竟,它+是一个二元运算符(所有fold需要或假设).

现在,在实践中,事实证明,Clojure的+被定义为比二进制运算符.它将需要许多甚至零值.凉.但如果我们使用这个"额外"信息,那么向读者发出信号是友好的. (apply + values)这样做 - 它说"我以一种奇怪的方式使用+,不仅仅是二元运算符".这有助于人们(至少我)理解代码.

[有趣的是,为什么apply感觉更清楚.而且我认为部分原因是你要对读者说:"看,+被设计为接受多个值(这是适用的用途),因此语言实现将包括零值的情况." reduce应用于单个列表时,隐式参数不存在.]

或者,(reduce + 0 values)也没关系.但(reduce + values)在我身上引发了一种本能的反应:"嗯,确实+提供零?".

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