• 【转】Scala:fold,foldLeft和foldRight区别与联系


    原文链接  http://www.iteblog.com/archives/1228

     从本质上说,fold函数将一种格式的输入数据转化成另外一种格式返回

    fold, foldLeft和foldRight这三个函数除了有一点点不同外,做的事情差不多。

    我将在下文解释它们的共同点并解释它们的不同点。   

    我将从一个简单的例子开始,用fold计算一系列整型的和。

    1 val numbers = List(5, 4, 8, 6, 2)
    2 numbers.fold(0) { (z, i) =>
    3   z + i
    4 }
    5 // result = 25

      List中的fold方法需要输入两个参数:初始值以及一个函数。输入的函数也需要输入两个参数:累加值和当前item的索引

    那么上面的代码片段发生了什么事?

    // scala 2.10.5
    // package scala.collection
    // trait LinearSeqOptimized
    
    override /*TraversableLike*/
      def foldLeft[B](z: B)(f: (B, A) => B): B = {
        var acc = z
        var these = this
        while (!these.isEmpty) {
          acc = f(acc, these.head)
          these = these.tail
        }
        acc
      }
    

      由上面的代码可知,传入的函数需要两个参数,第一个参数是累加值,

    第二个可以看作是一个普通的参数,只是每次都是these.head,并不是“当前item的索引”。

    代码开始运行的时候,初始值0作为第一个参数传进到fold函数中,list中的第一个item作为第二个参数传进fold函数中。   

    1、fold函数开始对传进的两个参数进行计算,在本例中,仅仅是做加法计算,然后返回计算的值;   

    2、Fold函数然后将上一步返回的值作为输入函数的第一个参数,并且把list中的下一个item作为第二个参数传进继续计算,同样返回计算的值;   

    3、第2步将重复计算,直到list中的所有元素都被遍历之后,返回最后的计算值,整个过程结束;   

    4、这虽然是一个简单的例子,让我们来看看一些比较有用的东西。

    早在后面将会介绍foldLeft函数,并解释它和fold之间的区别,

    目前,你只需要想象foldLeft函数和fold函数运行过程一样。

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

    1 class Foo(val name: String, val age: Int, val sex: Symbol)
    2  
    3 object Foo {
    4   def apply(name: String, age: Int, sex: Symbol) = new Foo(name, age, sex)
    5 }

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

    1 val fooList = Foo("Hugh Jass", 25, 'male) ::
    2               Foo("Biggus Dickus", 43, 'male) ::
    3               Foo("Incontinentia Buttocks", 37, 'female) ::
    4               Nil

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

    01 val stringList = fooList.foldLeft(List[String]()) { (z, f) =>
    02   val title = f.sex match {
    03     case 'male => "Mr."
    04     case 'female => "Ms."
    05   }
    06   z :+ s"$title ${f.name}, ${f.age}"
    07 }
    08  
    09 // stringList(0)
    10 // Mr. Hugh Jass, 25
    11  
    12 // stringList(2)
    13 // Ms. Incontinentia Buttocks, 37

      和第一个例子一样,我们也有个初始值,这里是一个空的String list,也有一个操作函数。

    在本例中,我们判断了性别,并构造了我们想要的String,并追加到累加器中(这里是一个list)。   

    fold, foldLeft, and foldRight之间的区别   

    主要的区别是fold函数操作遍历问题集合的顺序。

    foldLeft是从左开始计算,然后往右遍历。

    foldRight是从右开始算,然后往左遍历。

    而fold遍历的顺序没有特殊的次序。

    来看下这三个函数的实现吧(在TraversableOnce特质里面实现)

    01 def fold[A1 >: A](z: A1)(op: (A1, A1) => A1): A1 = foldLeft(z)(op)
    02  
    03 def foldLeft[B](z: B)(op: (B, A) => B): B = {
    04   var result = z
    05   this.seq foreach (x => result = op(result, x))
    06   result
    07 }
    08  
    09 def foldRight[B](z: B)(op: (A, B) => B): B =
    10   reversed.foldLeft(z)((x, y) => op(y, x))

      由于fold函数遍历没有特殊的次序,所以对fold的初始化参数和返回值都有限制。

    在这三个函数中,初始化参数和返回值的参数类型必须相同。   

    第一个限制是初始值的类型必须是list中元素类型的超类。

    在我们的例子中,我们的对List[Int]进行fold计算,而初始值是Int类型的,它是List[Int]的超类。   

    第二个限制是初始值必须是中立的(neutral)。也就是它不能改变结果。

    比如对加法来说,中立的值是0;而对于乘法来说则是1,对于list来说则是Nil。

      顺便说下,其实foldLeft和foldRight函数还有两个缩写的函数:
    1 def /:[B](z: B)(op: (B, A) => B): B = foldLeft(z)(op)
    2  
    3 def :[B](z: B)(op: (A, B) => B): B = foldRight(z)(op)
    4  
    5 scala> (0/:(1to 100))(_+_
    6 res32: Int = 5050
    7  
    8 scala> ((1to 100):0)(_+_)
    9 res24: Int = 5050
     def testFold = {
        println("test foldLeft")
        val t = 1 to 5 toList
        val r1 = t.fold(1016)((z, head) => { println(head); z - head })
        println(r1)
        println("test foldRight")
        val r2 = t.foldRight(1016)((head, z) => { println(head); z - head })
        println(r2)
        /**
         * test foldLeft
         * 1
         * 2
         * 3
         * 4
         * 5
         * 1001
         * test foldRight
         * 5
         * 4
         * 3
         * 2
         * 1
         * 1001
         */
      }
    

      

  • 相关阅读:
    #include <functional>
    3.3内联函数
    如何查看内存占用和运行速度
    属性和方法的动态绑定和限制
    __slots__节约空间
    函数进阶之一等对象
    python继承之super
    python的方法VSjava方法
    python面向对象基础(三)内置方法 __xx__
    python面向对象基础(二)反射
  • 原文地址:https://www.cnblogs.com/ihongyan/p/4733263.html
Copyright © 2020-2023  润新知