concat中foldRight和foldLeft之间的区别是什么?

时间:2016-11-11 11:42:59

标签: scala

为什么我不能在以下代码中使用fold Left:

def concatList[T](xs: List[T],ys:List[T]): List[T]=
     (xs foldLeft ys)(_::_)

实际上我很难理解foldRight和foldLeft之间的区别,有没有例子说明真正的差异?

感谢。

1 个答案:

答案 0 :(得分:5)

嗯,你可以,

scala> def concatList[T](xs: List[T],ys:List[T]) = 
         (xs foldLeft ys)( (a, b) => b :: a )
concatList: [T](xs: List[T], ys: List[T])List[T]

scala> concatList(List(1,2,3), List(6,7,8))
res0: List[Int] = List(3, 2, 1, 6, 7, 8)

这是你期待的结果吗?我不这么认为。

首先让我们看一下折叠的签名和::(仅用于说明目的的简化,但完全符合我们的情况):

given a List[T]
  def ::(v:T): List[T] // This is a right associative method, more below
  def foldLeft[R](r:R)(f: (R,T) => R):R
  def foldRight[R](r:R)(f: (T,R) => R):R

现在,在foldLeft我们xs.foldLeft(ys)中应用一个参数列表,并从foldLeft样本调用中统一我们签名中的类型:

List [T]:List [Int] ,因此 T:Int R:List [Int] ,适用于foldLeft签名给出

foldLeft[List[Int]](r:List[Int])( f:(List[Int],Int) => List[Int] )

现在,对于::的使用,a :: b编译为b.::(a),Scala经常将其称为右关联方法。对于以:结尾的方法,这是一种特殊的语法糖,在定义列表时非常方便:1 :: 2 :: Nil就像编写Nil.::(2).::(1)一样。

继续我们foldLeft的实例化,我们需要传递的函数必须如下所示:(List[Int],Int) => List[Int]。考虑(a,b) => a :: b,如果我们将其与我们f获取的类型统一起来:

a:列出[Int] b:Int ,将其与a2 :: b2 a2:Int , b2:列出[Int] 。为了编译,a和a2与b和b2一起必须具有相同的类型。他们没有!

注意在我的例子中,我颠倒了参数,使匹配的类型为b2,b匹配a2的类型。

我将提供另一个编译版本:

def concatList[T](xs: List[T],ys:List[T]) = (xs foldLeft ys)( _.::(_) )

要简短地讲述故事,请查看foldRight签名

def foldRight[R](r:R)(f: (T,R) => R):R

参数已被反转,因此使f = _ :: _为我们提供正确的类型。

哇,这是关于类型推断的很多解释,我很准时,但我还是要解释左右折叠的含义之间的区别。现在看看https://wiki.haskell.org/Fold,特别是这两个想象:

foldLeft foldRight

注意,foldl和foldr的参数是反转的,首先是函数,它们是签名中的初始参数r,而不是::的列表构造它只使用{{ 1}}。两个非常小的细节。