减less,折叠或扫描(左/右)?

什么时候应该使用reduceLeftreduceRightfoldLeftfoldRightscanLeftscanRight

我想要一个直觉/概述他们的分歧 – 可能有一些简单的例子。

一般而言,所有6个折叠函数都将二元运算符应用于集合的每个元素。 每个步骤的结果传递到下一步(作为二元运算符的两个参数之一的input)。 这样我们可以累积一个结果。

reduceLeftreduceRight累积一个结果。

foldLeftfoldRight使用foldRight累计单个结果。

scanLeftscanRight累积中间累积结果的集合。

积累

从左和向前…

通过元素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),我们会注意到现在我们的二元运算符的第二个参数累加了结果(运算符是相同的,我们只是切换参数名称以使其angular色清晰):

 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版本! 请记住,xRightvariables中的(减)累加值被传递给二元运算符的第二个参数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) 

最后一个列表(-2,3,-1,4,0)可能不是你所期望的!

正如你所看到的,你可以通过简单地运行一个scanX来检查你的foldX在做什么,并在每一步debugging累计结果。

底线

  • reduceLeftreduceRight结果。
  • 如果你有一个foldRightfoldLeftfoldRight一个结果。
  • 使用scanLeftscanRight累积中间结果的集合。

  • 如果你想要通过集合向前 ,请使用xLeft变体。

  • 如果您想要在整个集合中倒退 ,请使用xRight变体。

正常情况下,REDUCE,FOLD,SCAN方法通过在LEFT上累积数据并继续更改RIGHTvariables来工作。 他们之间的主要区别是减less,折叠是: –

折叠将始终以seed值开始,即用户定义的起始值。 Reduce会抛出一个exception,如果收集是空的,折叠会返回种子值。 总会产生一个单一的值。

扫描用于左侧或右侧的项目的一些处理顺序,然后我们可以利用以前的结果在后续的计算。 这意味着我们可以扫描项目。 总会产生一个集合。

  • LEFT_REDUCE方法与REDUCE方法类似。
  • RIGHT_REDUCE与reduceLeft相反,即它在RIGHT中累加值并不断改变左边的variables。

  • reduceLeftOption和reduceRightOption类似于left_reduce和right_reduce,区别在于它们返回OPTION对象的结果。

下面提到的代码的一部分输出是:

在数字列表上使用scan操作(使用seed0List(-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)List 0,2,3,3,2,0)

  • {2,0} => 2 {1,2} => 3 {0,3} => 3 {-1,3} => 2 {-2,2} => 0 scanRight(b + a)List 0,2,3,3,2,0)

在stringList("A","B","C","D","E")使用reducefold操作

  • (A + B)=> AB {AB,C} => ABC {ABC,D} => ABCD {ABCD,E} => ABCDE reduce
  • {A,B} => AB {AB,C} => ABC {ABC,D} => ABCD {ABCD,E} => ABCDE reduceLeft(a + b)ABCDE
  • {A,B} => BA {BA,C} => CBA {CBA,D} => DCBA {DCBA,E} => EDCBA reduceLeft(b + a)EDCB
  • {D,E} => DE {C,DE} => CDE {B,CDE} => BCDE {A,BCDE} => ABCDE reduceRight(a + b)ABCDE
  • {D,E} => ED {C,ED} => EDC {B,EDC} => EDCB {A,EDCB} => EDCBA reduceRight(b + a)EDCBA

代码:

 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})) }