Scala:fold,foldLeft和foldRight区别与联系

从本质上说,fold函数将一种格式的输入数据转化成另外一种格式返回。fold, foldLeft和foldRight这三个函数除了有一点点不同外,做的事情差不多。我将在下文解释它们的共同点并解释它们的不同点。
  我将从一个简单的例子开始,用fold计算一系列整型的和。

  val numbers = List(5, 4, 8, 6, 2)               //> numbers  : List[Int] = List(5, 4, 8, 6, 2)
  numbers.fold(0) { (z, i) =>
    z + i
  }                                               //> res8: Int = 25

List中的fold方法需要输入两个参数:初始值以及一个函数。输入的函数也需要输入两个参数:累加值和当前item的索引。那么上面的代码片段发生了什么事?
  代码开始运行的时候,初始值0作为第一个参数传进到fold函数中,list中的第一个item作为第二个参数传进fold函数中。
  1、fold函数开始对传进的两个参数进行计算,在本例中,仅仅是做加法计算,然后返回计算的值;
  2、Fold函数然后将上一步返回的值作为输入函数的第一个参数,并且把list中的下一个item作为第二个参数传进继续计算,同样返回计算的值;
  3、第2步将重复计算,直到list中的所有元素都被遍历之后,返回最后的计算值,整个过程结束;
  4、这虽然是一个简单的例子,让我们来看看一些比较有用的东西。早在后面将会介绍foldLeft函数,并解释它和fold之间的区别,目前,你只需要想象foldLeft函数和fold函数运行过程一样。

下面是一个简单的类和伴生类:

  class Foo(val name: String, val age: Int, val sex: Symbol)

  object Foo {
    def apply(name: String, age: Int, sex: Symbol) = new Foo(name, age, sex)
  }

假如我们有很多的Foo实例,并存在list中:


  val fooList = Foo("Hugh Jass", 25, 'male) ::
              Foo("Biggus Dickus", 43, 'male) ::
              Foo("Incontinentia Buttocks", 37, 'female) ::
              Nil                                 //> fooList  : List[test.Foo] = List(test.Foo@77caeb3e, test.Foo@1e88b3c, test.
                                                  //| Foo@42d80b78)

我们想将上面的list转换成一个存储[title] [name], [age]格式的String链表:


  val stringList = fooList.foldLeft(List[String]()) { (z, f) =>
      val title = f.sex match {
        case 'male => "Mr."
        case 'female => "Ms."
      }
      z :+ s"$title ${f.name}, ${f.age}"
    }                                         //> stringList  : List[String] = List(Mr. Hugh Jass, 25, Mr. Biggus Dickus, 43,
                                                  //|  Ms. Incontinentia Buttocks, 37)
  stringList(0)                                   //> res8: String = Mr. Hugh Jass, 25
  stringList(2)                                   //> res9: String = Ms. Incontinentia Buttocks, 37

和第一个例子一样,我们也有个初始值,这里是一个空的String list,也有一个操作函数。在本例中,我们判断了性别,并构造了我们想要的String,并追加到累加器中(这里是一个list)。
  fold, foldLeft, and foldRight之间的区别
  主要的区别是fold函数操作遍历问题集合的顺序。foldLeft是从左开始计算,然后往右遍历。foldRight是从右开始算,然后往左遍历。而fold遍历的顺序没有特殊的次序。来看下这三个函数的实现吧(在TraversableOnce特质里面实现)

  def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)

  def foldLeft[B](z: B)(op: (B, A) => B): B = {
    var result = z
    this.seq foreach (x => result = op(result, x))
    result
  }

  def foldRight[B](z: B)(op: (A, B) => B): B =
    reversed.foldLeft(z)((x, y) => op(y, x))

由于fold函数遍历没有特殊的次序,所以对fold的初始化参数和返回值都有限制。在这三个函数中,初始化参数和返回值的参数类型必须相同。
  第一个限制是初始值的类型必须是list中元素类型的超类。在我们的例子中,我们的对List[Int]进行fold计算,而初始值是Int类型的,它是List[Int]的超类。
  第二个限制是初始值必须是中立的(neutral)。也就是它不能改变结果。比如对加法来说,中立的值是0;而对于乘法来说则是1,对于list来说则是Nil。

顺便说下,其实foldLeft和foldRight函数还有两个缩写的函数:

  def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)

  def :\[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)

原文链接: 【Scala:fold,foldLeft和foldRight区别与联系】(https://www.iteblog.com/archives/1228.html)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 每个人 都不能逃避命运的选择 只有站在阳光下 你才能触摸黑暗 只要站在黑暗里 你也能看到光明 风吹不灭 火燃不尽 ...
    驽马渡江阅读 1,326评论 0 6
  • 阅读了斯科特·派克写的第三本关于心灵成长的书,似乎一切又都回到了源点,随着第三本书阅读的结尾,之前深抵内心的畅快感...
    品牌设计师曹骏彤阅读 4,474评论 0 0
  • 老师,我想对你说 老师还记得吗?当我们第一次踏进9班的这个大集体,一个个幼稚的脸庞和您那灿烂的笑容照相呼应 是您...
    090815阅读 1,378评论 0 0