注意:我在使用Scala 2.8 - 这可能是个问题吗?
为什么我不能像fold
或foldLeft
一样使用foldRight
功能?
在Set scaladoc中它说:
折叠的结果可能只是此并行集合的类型参数
T
的超类型。
但我在函数签名中看不到类型参数T
:
def fold [A1 >: A] (z: A1)(op: (A1, A1) ⇒ A1): A1
foldLeft-Right
和fold
之间有什么区别,我如何使用后者?
编辑:例如,如何编写折叠以添加列表中的所有元素?使用foldLeft
,它将是:
val foo = List(1, 2, 3)
foo.foldLeft(0)(_ + _)
// now try fold:
foo.fold(0)(_ + _)
>:7: error: value fold is not a member of List[Int]
foo.fold(0)(_ + _)
^
答案 0 :(得分:66)
简短回答:
foldRight
与右边相关联。即元素将按从右到左的顺序累积:
List(a,b,c).foldRight(z)(f) = f(a, f(b, f(c, z)))
foldLeft
左侧同伴。即将初始化累加器,并按从左到右的顺序将元素添加到累加器:
List(a,b,c).foldLeft(z)(f) = f(f(f(z, a), b), c)
fold
是 associative ,因为没有定义元素加在一起的顺序。即fold
的参数构成 monoid 。
答案 1 :(得分:53)
fold
和foldRight
相反, foldLeft
并未提供有关处理集合元素的顺序的任何保证。您可能希望使用具有更多约束签名的fold
和并行集合,其中缺少有保证的处理顺序有助于并行集合以并行方式实现折叠。更改签名的原因是类似的:通过附加约束,更容易进行并行折叠。
答案 2 :(得分:8)
你对Scala的旧版本是个问题是对的。如果您查看Scala 2.8.1的scaladoc page,您会看到没有定义折叠(这与您的错误消息一致)。显然,Scala 2.9中引入了fold
。
答案 3 :(得分:3)
对于您的特定示例,您将使用与foldLeft相同的方式对其进行编码。
val ns = List(1, 2, 3, 4)
val s0 = ns.foldLeft (0) (_+_) //10
val s1 = ns.fold (0) (_+_) //10
assert(s0 == s1)
答案 4 :(得分:3)
同意其他答案。考虑给出一个简单的说明性例子:
object MyClass {
def main(args: Array[String]) {
val numbers = List(5, 4, 8, 6, 2)
val a = numbers.fold(0) { (z, i) =>
{
println("fold val1 " + z +" val2 " + i)
z + i
}
}
println(a)
val b = numbers.foldLeft(0) { (z, i) =>
println("foldleft val1 " + z +" val2 " + i)
z + i
}
println(b)
val c = numbers.foldRight(0) { (z, i) =>
println("fold right val1 " + z +" val2 " + i)
z + i
}
println(c)
}
}
fold val1 0 val2 5
fold val1 5 val2 4
fold val1 9 val2 8
fold val1 17 val2 6
fold val1 23 val2 2
25
foldleft val1 0 val2 5
foldleft val1 5 val2 4
foldleft val1 9 val2 8
foldleft val1 17 val2 6
foldleft val1 23 val2 2
25
fold right val1 2 val2 0
fold right val1 6 val2 2
fold right val1 8 val2 8
fold right val1 4 val2 16
fold right val1 5 val2 20
25
答案 5 :(得分:0)
fold()进行并行处理,因此不能保证处理顺序。 就像foldLeft和foldRight一样,从左到右(对于foldLeft)或从右到左(对于foldRight)依次处理项目
列表总和的示例-
val numList = List(1, 2, 3, 4, 5)
val r1 = numList.par.fold(0)((acc, value) => {
println("adding accumulator=" + acc + ", value=" + value + " => " + (acc + value))
acc + value
})
println("fold(): " + r1)
println("#######################")
/*
* You can see from the output that,
* fold process the elements of parallel collection in parallel
* So it is parallel not linear operation.
*
* adding accumulator=0, value=4 => 4
* adding accumulator=0, value=3 => 3
* adding accumulator=0, value=1 => 1
* adding accumulator=0, value=5 => 5
* adding accumulator=4, value=5 => 9
* adding accumulator=0, value=2 => 2
* adding accumulator=3, value=9 => 12
* adding accumulator=1, value=2 => 3
* adding accumulator=3, value=12 => 15
* fold(): 15
*/
val r2 = numList.par.foldLeft(0)((acc, value) => {
println("adding accumulator=" + acc + ", value=" + value + " => " + (acc + value))
acc + value
})
println("foldLeft(): " + r2)
println("#######################")
/*
* You can see that foldLeft
* picks elements from left to right.
* It means foldLeft does sequence operation
*
* adding accumulator=0, value=1 => 1
* adding accumulator=1, value=2 => 3
* adding accumulator=3, value=3 => 6
* adding accumulator=6, value=4 => 10
* adding accumulator=10, value=5 => 15
* foldLeft(): 15
* #######################
*/
// --> Note in foldRight second arguments is accumulated one.
val r3 = numList.par.foldRight(0)((value, acc) => {
println("adding value=" + value + ", acc=" + acc + " => " + (value + acc))
acc + value
})
println("foldRight(): " + r3)
println("#######################")
/*
* You can see that foldRight
* picks elements from right to left.
* It means foldRight does sequence operation.
*
* adding value=5, acc=0 => 5
* adding value=4, acc=5 => 9
* adding value=3, acc=9 => 12
* adding value=2, acc=12 => 14
* adding value=1, acc=14 => 15
* foldRight(): 15
* #######################
*/
答案 6 :(得分:0)
有两种解决问题的方法,即迭代和递归。让我们通过一个简单的例子来理解。让我们编写一个函数来求和直到给定的数字。
例如,如果我将输入设为5,则应该得到15作为输出,如下所述。
输入:5
输出 :( 1 + 2 + 3 + 4 + 5)= 15
迭代解决方案。
重复1到5并求和每个元素。
def sumNumber(num: Int): Long = {
var sum=0
for(i <- 1 to num){
sum+=i
}
sum
}
递归解决方案
将较大的问题分解为较小的问题并加以解决。
def sumNumberRec(num:Int, sum:Int=0): Long = {
if(num == 0){
sum
}else{
val newNum = num - 1
val newSum = sum + num
sumNumberRec(newNum, newSum)
}
}
FoldLeft :是一种迭代解决方案
FoldRight :是一种递归解决方案 我不确定他们是否有备忘录来提高复杂性。
因此,如果您在小型列表上运行foldRight和FoldLeft,则两者都将为您提供性能相似的结果。
但是,如果您尝试在长列表上运行FoldRight
,则可能会引发 StackOverFlow 错误(取决于您的内存)
检查以下屏幕截图,其中foldLeft
正常运行,但是同一列表上的foldRight
给出了OutofMemmory
错误。