首页 文章

Scala中foldLeft和reduceLeft之间的区别

提问于
浏览
173

我已经学会了 foldLeftreduceLeft 之间的基本区别

foldLeft:

必须传递

  • 初始值

reduceLeft:

  • 将集合的第一个元素作为初始值
    如果集合为空,则
  • 抛出异常

还有其他区别吗?

有两种方法具有相似功能的任何特定原因?

7 回答

  • 42

    在给出实际答案之前,这里很少提及:

    • 你的问题与 left 没有任何关系,而是减少和折叠之间的区别

    • 差别不是实现,只需看一下签名即可 .

    • 这个问题并不是函数式编程的两个概念 .

    Back to your question:

    这是 foldLeft 的签名(对于我要提出的观点,也可能是 foldRight ):

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

    这里是 reduceLeft 的签名(这里方向无关紧要)

    def reduceLeft [B >: A] (f: (B, A) => B): B
    

    这两个看起来非常相似,从而引起了混乱 . reduceLeftfoldLeft 的一个特例(顺便说一句,你可以通过使用其中任何一个来表达相同的东西) .

    当你在 List[Int] 上调用 reduceLeft 时,它会将整个整数列表减少为单个值,这将是 Int 类型(或者是 Int 的超类型,因此 [B >: A] ) .

    当你在 List[Int] 上调用 foldLeft 时,它会将整个列表(想象滚动一张纸)折叠成单个值,但这个值不必与 Int (因此 [B] )相关 .

    这是一个例子:

    def listWithSum(numbers: List[Int]) = numbers.foldLeft((List[Int](), 0)) {
       (resultingTuple, currentInteger) =>
          (currentInteger :: resultingTuple._1, currentInteger + resultingTuple._2)
    }
    

    此方法采用 List[Int] 并返回 Tuple2[List[Int], Int](List[Int] -> Int) . 它计算总和并返回一个带有整数列表的元组,它的总和 . 顺便说一下,列表向后返回,因为我们使用了 foldLeft 而不是 foldRight .

    请观看One Fold to rule them all以获得更深入的解释 .

  • 5

    reduceLeft 只是一种方便的方法 . 它相当于

    list.tail.foldLeft(list.head)(_)
    
  • 2

    foldLeft 更通用,您可以使用它来生成与您最初放入的内容完全不同的内容 . 而 reduceLeft 只能生成相同类型或超类型集合类型的最终结果 . 例如:

    List(1,3,5).foldLeft(0) { _ + _ }
    List(1,3,5).foldLeft(List[String]()) { (a, b) => b.toString :: a }
    

    foldLeft 将使用最后折叠结果(第一次使用初始值)和下一个值应用闭包 .

    另一方面, reduceLeft 将首先组合列表中的两个值并将其应用于闭包 . 接下来,它将其余值与累积结果组合在一起 . 看到:

    List(1,3,5).reduceLeft { (a, b) => println("a " + a + ", b " + b); a + b }
    

    如果列表为空 foldLeft 可以将初始值显示为合法结果 . 另一方面,如果在列表中找不到至少一个值,则 reduceLeft 没有合法值 .

  • 0

    作为参考, reduceLeft 如果应用于具有以下错误的空容器将会出错 .

    java.lang.UnsupportedOperationException: empty.reduceLeft
    

    重新编写要使用的代码

    myList foldLeft(List[String]()) {(a,b) => a+b}
    

    是一个潜在的选择 . 另一种方法是使用 reduceLeftOption 变量返回Option包装结果 .

    myList reduceLeftOption {(a,b) => a+b} match {
      case None    => // handle no result as necessary
      case Some(v) => println(v)
    }
    
  • 175

    它们都在Scala标准库中的基本原因可能是因为它们都在Haskell标准库中(称为 foldlfoldl1 ) . 如果 reduceLeft 不是,则通常将其定义为不同项目中的便捷方法 .

  • 270

    来自Functional Programming Principles in Scala(Martin Odersky):

    函数reduceLeft是根据更通用的函数foldLeft定义的 . foldLeft类似于reduceLeft,但是接受一个累加器z作为附加参数,当在空列表上调用foldLeft时返回该参数:( List(x1,...,xn)foldLeft z)(op)=(...( z op x1)op ...)op x

    [与 reduceLeft 相对,在空列表中调用时抛出异常 . ]

    该课程(参见讲座5.5)提供了这些函数的抽象定义,这些定义说明了它们之间的差异,尽管它们在模式匹配和递归的使用方面非常相似 .

    abstract class List[T] { ...
      def reduceLeft(op: (T,T)=>T) : T = this match{
        case Nil     => throw new Error("Nil.reduceLeft")
        case x :: xs => (xs foldLeft x)(op)
      }
      def foldLeft[U](z: U)(op: (U,T)=>U): U = this match{
        case Nil     => z
        case x :: xs => (xs foldLeft op(z, x))(op)
      }
    }
    

    请注意, foldLeft 返回 U 类型的值,该类型的类型不一定与 List[T] 相同,但reduceLeft返回与列表类型相同的值 .

  • 4

    要真正理解你在使用fold / reduce做什么,请检查:http://wiki.tcl.tk/17983非常好的解释 . 一旦你得到折叠的概念,reduce将与上面的答案一起:list.tail.foldLeft(list.head)(_)

相关问题