Answers:
reduce
并且apply
对于需要在可变对数情况下查看其所有参数的关联函数,当然仅等效(就返回的最终结果而言)。当它们在结果上是等效的时,我会说这apply
总是完全惯用的,而reduce
在等效情况下,它是等效的,并且在许多常见情况下可能会眨眼之间。接下来是我相信这一点的理由。
+
它本身是reduce
针对可变变量情况(超过2个参数)实现的。的确,对于任何可变参数关联功能,这似乎都是一种非常明智的“默认”方式:reduce
可能会进行一些优化以加快处理速度-可能是通过诸如internal-reduce
最近在master中禁用的1.2新奇功能之类的方法实现的,但是希望在将来重新引入-在每个函数中复制可能很愚蠢,在vararg情况下可能会从中受益。在这种常见情况下,apply
只会增加一点开销。(请注意,不必担心。)
另一方面,一个复杂的函数可能会利用一些优化机会,而这些优化机会通常不够内置reduce
。然后apply
让您利用这些机会,而reduce
这实际上可能会降低您的速度。一个实际情况下后一种情况的很好的例子提供了str
:它在StringBuilder
内部使用,将从中使用apply
而不是从中受益reduce
。
所以,apply
如果有疑问,我会说使用。如果您碰巧知道它并没有给您买完任何东西reduce
(而且这种情况不太可能很快改变),请随意使用它reduce
来减少不必要的少量开销。
sum
,我想说Clojure具有此功能,可以调用+
它,并且可以与一起使用apply
。:-)认真地说,我认为在Lisp中,一般来说,如果提供了可变函数,通常不会伴随对集合进行操作的包装程序-这就是您使用的apply
功能(或者reduce
,如果您知道更有意义的话)。
reduce
当有疑问时,apply
当您确定知道有优化时。reduce
的合同更精确,因此更容易进行一般优化。apply
更模糊,因此只能根据具体情况进行优化。str
和concat
是两个普遍的例外。
reduce
和apply
在结果上等效的函数,我希望有问题的函数的作者知道如何最好地优化其可变参数重载,并仅在reduce
if 方面实现它。确实,这才是最有意义的选择(这样做的选择当然总是可用的,并且是非常明智的默认选择)。不过,我确实知道您来自哪里,这reduce
绝对是Clojure的绩效故事的核心(而且越来越重要),而且经过高度优化和明确说明。
使用+等简单函数时,实际上使用哪一个都没有关系。
通常,此想法reduce
是一个累加操作。您向累加函数提供当前累加值和一个新值。该函数的结果是下一次迭代的累加值。因此,您的迭代看起来像:
cum-val[i+1] = F( cum-val[i], input-val[i] ) ; please forgive the java-like syntax!
对于apply,这个想法是您试图调用一个期望有多个标量参数的函数,但是它们目前在一个集合中,需要被拉出。因此,与其说:
vals = [ val1 val2 val3 ]
(some-fn (vals 0) (vals 1) (vals 2))
我们可以说:
(apply some-fn vals)
并转换为等效于:
(some-fn val1 val2 val3)
因此,使用“ apply”就像在序列周围“删除括号”。
在这个话题上有点晚了,但是在阅读了这个例子之后我做了一个简单的实验。这是我的代表的结果,我只是无法从响应中推断出任何内容,但是在reduce和apply之间似乎存在某种缓存作用。
user=> (time (reduce + (range 1e3)))
"Elapsed time: 5.543 msecs"
499500
user=> (time (apply + (range 1e3)))
"Elapsed time: 5.263 msecs"
499500
user=> (time (apply + (range 1e4)))
"Elapsed time: 19.721 msecs"
49995000
user=> (time (reduce + (range 1e4)))
"Elapsed time: 1.409 msecs"
49995000
user=> (time (reduce + (range 1e5)))
"Elapsed time: 17.524 msecs"
4999950000
user=> (time (apply + (range 1e5)))
"Elapsed time: 11.548 msecs"
4999950000
查看clojure的源代码可以通过内部缩减来减少其相当干净的递归,尽管在apply的实现中未发现任何问题。+的Clojure实现适用于内部调用reduce,由repl缓存,这似乎解释了第4个调用。有人可以澄清这里到底发生了什么吗?
range
呼叫放在time
表单内。将其放在外面以消除序列构建的干扰。就我而言,reduce
始终表现出色apply
。
sum
haskell这样的内置函数?似乎很普通的操作。