缩小,折叠或扫描(向左/向右)? [英] Reduce, fold or scan (Left/Right)?

查看:89
本文介绍了缩小,折叠或扫描(向左/向右)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

何时应使用reduceLeftreduceRightfoldLeftfoldRightscanLeftscanRight?

When should I use reduceLeft, reduceRight, foldLeft, foldRight, scanLeft or scanRight?

我希望对它们之间的差异有一个直观的了解/概述-可能带有一些简单的示例.

I want an intuition/overview of their differences - possibly with some simple examples.

推荐答案

通常,所有6折函数将二进制运算符应用于集合的每个元素.每个步骤的结果都将传递到下一步(作为对二进制运算符的两个参数之一的输入).这样,我们可以累积结果.

In general, all 6 fold functions apply a binary operator to each element of a collection. The result of each step is passed on to the next step (as input to one of the binary operator's two arguments). This way we can cumulate a result.

reduceLeftreduceRight累计一个结果.

foldLeftfoldRight使用起始值累计单个结果.

foldLeft and foldRight cumulate a single result using a start value.

scanLeftscanRight使用起始值累积中间累积结果的集合.

scanLeft and scanRight cumulate a collection of intermediate cumulative results using a start value.

从左向后...

有了元素abc的集合和二进制运算符add,我们可以探索从集合的LEFT元素(从A到C)前进时不同的折叠函数的作用:

With a collection of elements abc and a binary operator add we can explore what the different fold functions do when going forwards from the LEFT element of the collection (from A to 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),我们会注意到现在二元运算符的 second 参数会累加结果(该运算符是相同的,只是切换了参数名称以使其作用清楚):

If we start with the RIGHT element and go backwards (from C to A) we'll notice that now the second argument to our binary operator accumulates the result (the operator is the same, we just switched the argument names to make their roles clear):

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元素开始通过减法来去累积,那么我们将通过二进制运算符minus的第一个参数res来累积结果:

If instead we were to de-cumulate some result by subtraction starting from the LEFT element of a collection, we would cumulate the result through the first argument res of our binary operator 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变量中的(去)累加值将传递给我们的二进制运算符minus second 参数res:

But look out for the xRight variations now! Remember that the (de-)cumulated value in the xRight variations is passed to the second parameter res of our binary operator 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)可能不是您直觉所期望的!

The last List(-2, 3, -1, 4, 0) is maybe not what you would intuitively expect!

如您所见,您可以通过简单地运行scanX来检查foldX的功能,并在每个步骤中调试累积的结果.

As you see, you can check what your foldX is doing by simply running a scanX instead and debug the cumulated result at each step.

  • 使用reduceLeftreduceRight累积结果.
  • 如果有起始值,请使用foldLeftfoldRight累加结果.
  • 使用scanLeftscanRight累积中间结果的集合.

  • Cumulate a result with reduceLeft or reduceRight.
  • Cumulate a result with foldLeft or foldRight if you have a start value.
  • Cumulate a collection of intermediate results with scanLeft or scanRight.

如果要遍历集合,请使用xLeft变体.

Use a xLeft variation if you want to go forwards through the collection.

这篇关于缩小,折叠或扫描(向左/向右)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆