Clojure:减少与应用

dby*_*rne 123 clojure

我理解reduce和之间的概念差异apply:

(reduce + (list 1 2 3 4 5))
; translates to: (+ (+ (+ (+ 1 2) 3) 4) 5)

(apply + (list 1 2 3 4 5))
; translates to: (+ 1 2 3 4 5)
Run Code Online (Sandbox Code Playgroud)

然而,哪一个更惯用的clojure?这种方式或其他方式有什么不同吗?从我的(有限的)性能测试来看,似乎reduce更快一些.

Mic*_*zyk 119

reduce并且apply当然只对于需要在变量arity情况下查看所有参数的关联函数而言(仅就返回的最终结果而言)是等效的.当它们在结果方面相当时,我会说这apply总是完全惯用的,而reduce在很多常见的情况下,它可能是相同的 - 并且可能会在很短的时间内完成.以下是我相信这一点的理由.

+本身是reduce针对变量案例(超过2个参数)实现的.事实上,对于任何变量,关联函数来说,这似乎是一种非常明智的"默认"方式:reduce有可能执行一些优化以加快速度 - 可能通过internal-reduce最近在主人身上禁用的1.2新颖性,但是希望将来能够重新引入 - 在vararg案例中,在每个可能从中受益的函数中复制将是愚蠢的.在这种常见情况下,apply只会增加一点开销.(注意没什么好担心的.)

另一方面,复杂的功能可能会利用一些不够通用的优化机会reduce; 然后apply会让你利用那些reduce可能实际上减慢你的速度.在实践中出现的后一种情况的一个很好的例子是str:它在StringBuilder内部使用,并且将从使用apply而不是使用中获益reduce.

所以,我apply怀疑时会说使用; 如果你碰巧知道它不会给你带来任何东西reduce(并且这不太可能很快改变),reduce如果你愿意,可以随意使用来减少这种微小的不必要的开销.

  • 谢谢,很高兴听到这个!Re:`sum`,我会说Clojure有这个函数,它叫做`+`,你可以用`apply`来实现它.:-)说实话,我认为在Lisp中,一般来说,如果提供了一个可变函数,它通常不会附带一个对集合进行操作的包装器 - 这就是你使用`apply`来实现(或者`reduce`,如果你知道的话这更有意义). (15认同)
  • 有趣的是,我的建议正好相反:`reduce`如有疑问,`apply`当你确定知道有优化时.`reduce`的契约更精确,因此更容易进行一般优化."apply"更加模糊,因此只能根据具体情况进行优化.`str`和`concat`是两个普遍的除外. (5认同)

Dav*_*ala 49

对于看这个答案的新手,
要小心,他们不一样:

(apply hash-map [:a 5 :b 6])
;= {:a 5, :b 6}
(reduce hash-map [:a 5 :b 6])
;= {{{:a 5} :b} 6}
Run Code Online (Sandbox Code Playgroud)


drc*_*ode 20

意见各不相同 - 在更大的Lisp世界中,reduce绝对被认为是更惯用的.首先,已经讨论过可变问题.此外,一些Common Lisp编译器在apply应用于非常长的列表时实际上会失败,因为它们处理参数列表的方式.

然而,在我的圈子中的Clojurists中,apply在这种情况下使用似乎更常见.我发现它更容易理解并且也更喜欢它.


G__*_*G__ 19

它在这种情况下没有区别,因为+是一个特殊情况,可以应用于任意数量的参数.Reduce是一种将需要固定数量的参数(2)的函数应用于任意长的参数列表的方法.


mik*_*era 9

我通常发现自己更喜欢使用reduce来处理任何类型的集合 - 它表现良好,并且通常是非常有用的功能.

我将使用apply的主要原因是,如果参数在不同的位置意味着不同的东西,或者如果你有一些初始参数但想要从集合中获取其余参数,例如

(apply + 1 2 other-number-list)
Run Code Online (Sandbox Code Playgroud)


mas*_*cip 8

在这种特殊情况下,我更喜欢,reduce因为它更具可读性:当我阅读时

(reduce + some-numbers)
Run Code Online (Sandbox Code Playgroud)

我立即知道你正在将一个序列变成一个值.

随着apply我要考虑的是被应用于其功能:"啊,它的+功能,所以我得到...单号".略显简单明了.


Ala*_*son 6

当使用像+这样的简单函数时,使用哪一个并不重要.

一般来说,这个想法reduce是一个累积操作.您将当前累积值和一个新值呈现给累积函数.函数的结果是下一次迭代的累积值.所以,你的迭代看起来像:

cum-val[i+1] = F( cum-val[i], input-val[i] )    ; please forgive the java-like syntax!
Run Code Online (Sandbox Code Playgroud)

对于apply,这个想法是你试图调用一个期望一些标量参数的函数,但它们目前在一个集合中,需要被拔出.所以,而不是说:

vals = [ val1 val2 val3 ]
(some-fn (vals 0) (vals 1) (vals 2))
Run Code Online (Sandbox Code Playgroud)

我们可以说:

(apply some-fn vals)
Run Code Online (Sandbox Code Playgroud)

它被转换为相当于:

(some-fn val1 val2 val3)
Run Code Online (Sandbox Code Playgroud)

因此,使用"apply"就像序列周围的"删除括号".


Ira*_*Ira 5

apply 的美妙之处在于给定函数(在本例中为 +)可以应用于由预先挂起的中间参数和结尾集合形成的参数列表。Reduce 是一个抽象,用于处理集合项,为每个集合项应用函数,并且不适用于变量 args 的情况。

(apply + 1 2 3 [3 4])
=> 13
(reduce + 1 2 3 [3 4])
ArityException Wrong number of args (5) passed to: core/reduce  clojure.lang.AFn.throwArity (AFn.java:429)
Run Code Online (Sandbox Code Playgroud)