【问题标题】:What 's the difference between foldRight and foldLeft in concatconcat中foldRight和foldLeft有什么区别
【发布时间】:2017-03-25 15:27:43
【问题描述】:

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

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

其实我很难理解 foldRight 和 foldLeft 之间的区别,有没有例子可以说明真正的区别?

谢谢。

【问题讨论】:

  • 一个从左到右,另一个从右到左。

标签: scala


【解决方案1】:

嗯,你可以,

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 : IntR : 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 : List[Int]b : Int,与a2 :: b2 的签名比较,a2 : Intb2 : 列表[Int]。为了编译,a 和 a2 以及 b 和 b2 必须各自具有相同的类型。他们没有!

请注意,在我的示例中,我反转了参数,使 a 匹配 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,特别是这两个想象:

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

【讨论】:

    猜你喜欢
    • 2011-09-09
    • 1970-01-01
    • 2013-11-10
    • 1970-01-01
    • 2017-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    相关资源
    最近更新 更多