foldLeft v。foldRight - 这有什么关系吗? [英] foldLeft v. foldRight - does it matter?
问题描述
以前, Nicolas Rinaudo 在Scala的列表foldRight总是使用foldLeft?
现在学习Haskell,我的理解是<在 ::
的情况下,code> foldRight 优先于 foldLeft
prepend)可以用于 ++
(追加)。
据我所知,理由是性能 - 前者出现在 O(1)
中,即添加一个项目在前面 - 恒定时间。而后者需要 O(N)
,即遍历整个列表并添加一个项目。
在Scala ,假设 foldLeft
是以 foldRight
的形式实现的,那么使用:+ over
++
与 foldRight
甚至自 foldLeft'd
?
例如,考虑这个简单的 fold ..
操作用于简单地返回列表中的元素。
foldLeft
折叠在每个元素上,通过:+
将每个元素添加到列表中。
阶>列表(foo,bar)。foldLeft(List [String]()){
(acc,elem)=> acc:+ elem}
res9:List [String] = List(foo,bar)
foldRight
在每个项目上执行foldLeft并使用 ::
运算符,但是然后反转。
scala>列表(foo,bar)。foldRight(List [String]()){
(elem,acc)=> elem :: acc}
res10:List [String] = List(foo,bar)
实际上,在Scala中使用 foldLeft
或 foldRight
来使用给定的 foldRight 使用
foldRight
?
<@> Henrichs的答案实际上与Scala无关,因为Scala对 foldLeft
和 foldRight
的实现是完全不同的(对于初学者,Scala有急切的评价)。
foldLeft
和 foldRight
自己对于你的程序的性能实际上很少做。两者都是(自由地说)O(n * c_f),其中c_f是给出的函数 f
的一次调用的复杂度。不过, foldRight
由于额外的反向
,因此会减慢一个常数。
因此,区分彼此的真正因素是您提供的匿名函数的复杂性。有时候,编写一个有效的函数可以更容易,该函数可以用于处理 foldLeft
,有时还可以处理 foldRight
。在你的例子中, foldRight
版本是最好的,因为你给 foldRight
的匿名函数是O(1) 。相比之下,您给 foldLeft
的匿名函数是O(n)本身(摊销,这是重要的),因为 acc
从0增长到n-1,并追加到n个元素列表中的是O(n)。 是否选择
foldLeft
或 foldRight
,但不是因为这些函数本身,而是因为匿名给他们的功能。如果两者都相同,默认情况下选择 foldLeft
。
Previously, Nicolas Rinaudo answered my question on Scala's List foldRight Always Using foldLeft?
Studying Haskell currently, my understanding is that foldRight
should be preferred over foldLeft
in cases where ::
(prepend) can be used over ++
(append).
The reason, as I understand, is performance - the former occurs in O(1)
, i.e. add an item to the front - constant time. Whereas the latter requires O(N)
, i.e. go through the whole list and add an item.
In Scala, given that foldLeft
is implemented in terms of foldRight
, does the benefit of using :+
over ++
with foldRight
even matter since foldRight
gets reversed, and then foldLeft'd
?
As an example, consider this simple fold..
operation for simply returning a list's elements in order.
foldLeft
folds over each element, adding each item to the list via :+
.
scala> List("foo", "bar").foldLeft(List[String]()) {
(acc, elem) => acc :+ elem }
res9: List[String] = List(foo, bar)
foldRight
performs a foldLeft with ::
operator on each item, but then reverses.
scala> List("foo", "bar").foldRight(List[String]()) {
(elem, acc) => elem :: acc }
res10: List[String] = List(foo, bar)
In reality, does it matter in Scala which foldLeft
or foldRight
to use given that foldRight
uses foldRight
?
@Rein Henrichs' answer is indeed irrelevant to Scala, because Scala's implementation of foldLeft
and foldRight
is completely different (for starters, Scala has eager evaluation).
foldLeft
and foldRight
themselves have actually very little to do wrt the performance of your program. Both are (liberally speaking) O(n*c_f) where c_f is the complexity of one call to the function f
that is given. foldRight
is slower by a constant factor because of the additional reverse
, though.
So the real factor that differentiates one from the other is the complexity of the anonymous function that you give. Sometimes, it is easier to write an efficient function designed to work with foldLeft
, and sometimes to foldRight
. In your example, the foldRight
version is best, because the anonymous function that you give to foldRight
is O(1). In contrast, the anonymous function that you give to foldLeft
is O(n) itself (amortized, which is what matters here), because acc
keeps growing from 0 to n-1, and appending to a list of n elements is O(n).
So it actually matters whether you choose foldLeft
or foldRight
, but not because of these functions themselves, but because of the anonymous functions given to them. If both are equivalent, choose foldLeft
by default.
这篇关于foldLeft v。foldRight - 这有什么关系吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!