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

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

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

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

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

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

1 val numbers = List(54862)
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   :+ 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): = {
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): =
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): = foldLeft(z)(op)
2  
3 def :\[B](z: B)(op: (A, 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
     */
  }

  

时间: 2024-11-02 23:41:58

【转】Scala:fold,foldLeft和foldRight区别与联系的相关文章

Scala深入浅出实战经典之 List的foldLeft、foldRight、sort操作代码实战

Scala 深入浅出实战经典(1-64讲)完整视频.PPT.代码下载:百度云盘:http://pan.baidu.com/s/1c0noOt6 腾讯微云:http://url.cn/TnGbdC 360云盘:http://yunpan.cn/cQ4c2UALDjSKy   访问密码 45e2 以下为第37讲List的foldLeft.foldRight.sort操作代码实战 package com.parllay.scala.dataset /** * Created by richard on

scala - 从合并两个Map说开去 - foldLeft 和 foldRight 还有模式匹配

开发中遇到需求:合并两个Map集合对象(将两个对应KEY的值累加) 先说解决方案: ( map1 /: map2 ) { case (map, (k,v)) => map + ( k -> (v + map.getOrElse(k, 0)) ) } 这特么什么鬼  (╯‵□′)╯""┻━┻☆))>○<) ......莫急,且听我慢慢道来......... 首先: Scala中现有的合并集合操作不能满足这个需求 . 注意合并后的结果a的G02的值其实是被覆盖掉了.

从合并两个Map说开去 - foldLeft 和 foldRight 还有模式匹配

开发中遇到需求:合并两个Map集合对象(将两个对应Key的值累加) 先说解决方案: ( map1 /: map2 ) { case (map, (k,v)) => map + ( k -> (v + map.getOrElse(k, 0)) ) } 首先: Scala中现有的合并集合操作不能满足这个需求 . 注意合并后的结果a的G02的值其实是被覆盖掉了.. 然后: 说说那个表达式中(a /: b)( ... ) 这部分是什么鬼.这个其实是scala简化的foldLeft函数. 先看foldL

Swift中的类似scala的foldleft方法的实现

extension Array {     func foldLeft(a:T, action:(T, T)->T)->T {         var ret:T = a         for b:T in self {             ret = action(ret,b)         }         return ret     } } 把这个放到项目当中任何一个swift文件当中,即可全局访问. 示例: 求最小值 var t = [1,3,5,4,9,100,6,-2]

scala - fold,aggregate,iterator

import org.json4s._ import org.json4s.jackson._ import org.json4s.jackson.JsonMethods._ import org.json4s.JsonDSL._ import org.json4s.JsonDSL.map2jvalue // or //import org.json4s.JsonDSL.WithDouble._ //import org.json4s.JsonDSL.WithBigDecimal._ //imp

第37讲:List的foldLeft、foldRight、sort操作代码实战

其实flodLeft和foldRight就是折叠操作,我让们看下下列的函数 折叠操作 def sum(xs:List[Int]):Int = ( 0 /: xs)(_ +_) def product(xs:List[Int]):Int=( 1 /: xs)(_ *_) (fruit.head /: fruit.tail)(_ + " " + _) //用空格连接列表中每个元素 列表排序 sort println(List(1,-4,3,2,6).sortWith(_<_))//L

[译]Scala中方法和函数的区别

在Scala中函数和方法有什么区别 方法可以作为一个表达式的一部分出现(调用函数并传参),但是方法(带参方法)不能作为最终的表达式, 但是函数可以作为最终的表达式出现: scala> //定义一个方法 scala> def m(x:Int) = 2*x m: (x: Int)Int scala> //定义一个函数 scala> val f = (x:Int) => 2*x f: Int => Int = <function1> scala> //方法不

[译]Scala方法和函数的区别

Scala中既有函数也有方法,大多数情况下我们都可以不去理会他们之间的区别.但是有时候我们必须要了解他们之间的不同. Scala中的方法跟Java的方法一样,方法是组成类的一部分.方法有名字.类型签名,有时方法上还有注解,以及方法的功能 实现代码(字节码). Scala中的函数是一个完整的对象.Scala中用22个特质(trait)抽象出了函数的概念.这22特质从Function1到Function22 如上图中的Function10代表的是:有10个形参,返回值为R(协变)的函数. Scala

scala def/val/lazy val区别以及call-by-name和call-by-value

scala 学习 参考链接:https://blog.csdn.net/qq_29343201/article/details/56281777 关于def/val/lazy val def def类似于每一次重新赋值,如果是用def定义函数,则是每一次重新获得一个函数 val 获得一次,并立即执行(严格执行) lazy val 惰性执行,也就是赋值(绑定)的时候先不会执行,等到需要的时候再执行 实验 scala> def f = {println("hello"); 1.0}