什么时候应该使用reduceLeft
,reduceRight
,foldLeft
,foldRight
,scanLeft
或scanRight
?
我希望对它们之间的差异有一个直观的认识/概述-可能有一些简单的例子。
reduce
和之间的区别fold
不是起始值的存在-而是由更深层的数学原因导致的。
什么时候应该使用reduceLeft
,reduceRight
,foldLeft
,foldRight
,scanLeft
或scanRight
?
我希望对它们之间的差异有一个直观的认识/概述-可能有一些简单的例子。
reduce
和之间的区别fold
不是起始值的存在-而是由更深层的数学原因导致的。
Answers:
通常,所有6折函数都将二进制运算符应用于集合的每个元素。每个步骤的结果都将传递到下一步(作为对二进制运算符的两个参数之一的输入)。这样我们可以累计结果。
reduceLeft
并reduceRight
累计一个结果。
foldLeft
并foldRight
使用起始值累计单个结果。
scanLeft
并scanRight
使用起始值累计中间累积结果的集合。
从左到右...
使用元素集合abc
和二进制运算符,add
我们可以探索从集合的LEFT元素(从A到C)前进时不同的折叠函数的作用:
val abc = List("A", "B", "C")
def add(res: String, x: String) = {
println(s"op: $res + $x = ${res + x}")
res + x
}
abc.reduceLeft(add)
// op: A + B = AB
// op: AB + C = ABC // accumulates value AB in *first* operator arg `res`
// res: String = ABC
abc.foldLeft("z")(add) // with start value "z"
// op: z + A = zA // initial extra operation
// op: zA + B = zAB
// op: zAB + C = zABC
// res: String = zABC
abc.scanLeft("z")(add)
// op: z + A = zA // same operations as foldLeft above...
// op: zA + B = zAB
// op: zAB + C = zABC
// res: List[String] = List(z, zA, zAB, zABC) // maps intermediate results
从右向后...
如果我们从RIGHT元素开始,然后倒退(从C到A),我们会注意到现在二进制运算符的第二个参数会累加结果(该运算符是相同的,我们只是切换了参数名称以使其角色清楚):
def add(x: String, res: String) = {
println(s"op: $x + $res = ${x + res}")
x + res
}
abc.reduceRight(add)
// op: B + C = BC
// op: A + BC = ABC // accumulates value BC in *second* operator arg `res`
// res: String = ABC
abc.foldRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: String = ABCz
abc.scanRight("z")(add)
// op: C + z = Cz
// op: B + Cz = BCz
// op: A + BCz = ABCz
// res: List[String] = List(ABCz, BCz, Cz, z)
。
从左到右...
相反,如果我们要从集合的LEFT元素开始通过减法来对某些结果进行累加,那么我们将通过res
二进制运算符的第一个参数来对结果进行累加minus
:
val xs = List(1, 2, 3, 4)
def minus(res: Int, x: Int) = {
println(s"op: $res - $x = ${res - x}")
res - x
}
xs.reduceLeft(minus)
// op: 1 - 2 = -1
// op: -1 - 3 = -4 // de-cumulates value -1 in *first* operator arg `res`
// op: -4 - 4 = -8
// res: Int = -8
xs.foldLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: Int = -10
xs.scanLeft(0)(minus)
// op: 0 - 1 = -1
// op: -1 - 2 = -3
// op: -3 - 3 = -6
// op: -6 - 4 = -10
// res: List[Int] = List(0, -1, -3, -6, -10)
从右向后...
但是,现在请注意xRight版本!请记住,xRight变量中的(去)累加值被传递给我们的二进制运算符的第二个参数:res
minus
def minus(x: Int, res: Int) = {
println(s"op: $x - $res = ${x - res}")
x - res
}
xs.reduceRight(minus)
// op: 3 - 4 = -1
// op: 2 - -1 = 3 // de-cumulates value -1 in *second* operator arg `res`
// op: 1 - 3 = -2
// res: Int = -2
xs.foldRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: Int = -2
xs.scanRight(0)(minus)
// op: 4 - 0 = 4
// op: 3 - 4 = -1
// op: 2 - -1 = 3
// op: 1 - 3 = -2
// res: List[Int] = List(-2, 3, -1, 4, 0)
最后一个List(-2、3,-1、4、0)可能不是您直觉所期望的!
如您所见,您可以通过简单地运行scanX来检查foldX的功能,并在每个步骤中调试累积的结果。
reduceLeft
或累加结果reduceRight
。foldLeft
或foldRight
如果您有一个起始值来累计结果。用scanLeft
或累积中间结果的集合scanRight
。
如果要向前浏览集合,请使用xLeft变体。
List
并然后应用foldLeft
。其他集合可能会实施不同的策略。通常,如果foldLeft
和foldRight
可以互换使用(所应用算符的缔合属性),则foldLeft
效率更高,更可取。
通常,REDUCE,FOLD,SCAN方法通过在LEFT上累积数据并继续更改RIGHT变量而起作用。它们之间的主要区别是REDUCE,FOLD是:
折叠始终以一个seed
值开始,即用户定义的起始值。如果collection为空,并且fold会返回种子值,那么Reduce会抛出异常。将始终产生单个值。
扫描用于从左侧或右侧对项目的某些处理顺序,然后我们可以在后续计算中利用先前的结果。这意味着我们可以扫描物品。总是会产生一个集合。
RIGHT_REDUCE与reduceLeft相反,即它在RIGHT中累积值并继续更改left变量。
reduceLeftOption和reduceRightOption与left_reduce和right_reduce相似,唯一的区别是它们在OPTION对象中返回结果。
以下代码的部分输出将是:-
scan
对数字列表使用操作(使用seed
值0
)List(-2,-1,0,1,2)
{0,-2} =>-2 {-2,-1} =>-3 {-3,0} =>-3 {-3,1} =>-2 {-2,2} => 0扫描列表(0,-2,-3,-3,-2,0)
{0,-2} =>-2 {-2,-1} =>-3 {-3,0} =>-3 {-3,1} =>-2 {-2,2} => 0 scanLeft(a + b)列表(0,-2,-3,-3,-2,0)
{0,-2} =>-2 {-2,-1} =>-3 {-3,0} =>-3 {-3,1} =>-2 {-2,2} => 0 scanLeft(b + a)列表(0,-2,-3,-3,-2,0)
{2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 scanRight(a + b)列表( 0、2、3、3、2、0)
{2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 scanRight(b + a)列表( 0、2、3、3、2、0)
使用reduce
,fold
对字符串列表进行操作List("A","B","C","D","E")
代码:
object ScanFoldReduce extends App {
val list = List("A","B","C","D","E")
println("reduce (a+b) "+list.reduce((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (a+b) "+list.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (b+a) "+list.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println("reduceRight (a+b) "+list.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("reduceRight (b+a) "+list.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" ")
b+a
}))
println("scan "+list.scan("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (a+b) "+list.scanLeft("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (b+a) "+list.scanLeft("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println("scanRight (a+b) "+list.scanRight("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanRight (b+a) "+list.scanRight("[")((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
//Using numbers
val list1 = List(-2,-1,0,1,2)
println("reduce (a+b) "+list1.reduce((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (a+b) "+list1.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" ")
a+b
}))
println("reduceLeft (b+a) "+list1.reduceLeft((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println(" reduceRight (a+b) "+list1.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println(" reduceRight (b+a) "+list1.reduceRight((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" ")
b+a
}))
println("scan "+list1.scan(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (a+b) "+list1.scanLeft(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b
}))
println("scanLeft (b+a) "+list1.scanLeft(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (b+a)+" " )
b+a
}))
println("scanRight (a+b) "+list1.scanRight(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
a+b}))
println("scanRight (b+a) "+list1.scanRight(0)((a,b)=>{
print("{"+a+","+b+"}=>"+ (a+b)+" " )
b+a}))
}
对于具有元素x0,x1,x2,x3和任意函数f的集合x,您具有以下内容:
1. x.reduceLeft (f) is f(f(f(x0,x1),x2),x3) - notice 3 function calls
2. x.reduceRight (f) is f(f(f(x3,x2),x1),x0) - notice 3 function calls
3. x.foldLeft (init,f) is f(f(f(f(init,x0),x1),x2),x3) - notice 4 function calls
4. x.foldRight(init,f) is f(f(f(f(init,x3),x2),x1),x0) - notice 4 function calls
5. x.scanLeft (init,f) is f(init,x0)=g0
f(f(init,x0),x1) = f(g0,x1) = g1
f(f(f(init,x0),x1),x2) = f(g1,x2) = g2
f(f(f(f(init,x0),x1),x2),x3) = f(g2,x3) = g3
- notice 4 function calls but also 4 emitted values
- last element is identical with foldLeft
6. x.scanRight (init,f) is f(init,x3)=h0
f(f(init,x3),x2) = f(h0,x2) = h1
f(f(f(init,x3),x2),x1) = f(h1,x1) = h2
f(f(f(f(init,x3),x2),x1),x0) = f(h2,x0) = h3
- notice 4 function calls but also 4 emitted values
- last element is identical with foldRight
scan
就像fold
但也发出所有中间值reduce
不需要初始值,有时很难找到fold
需要一个很难找到的初始值:
x.reduceLeft(f) === x.drop(1).foldLeft(x.head,f)
x.foldRight(init,f) === x.reverse.foldLeft(init,f)
x.foldLeft(init,f) === x.scanLeft(init,f).last