为什么我不能在以下代码中使用fold Left:
def concatList[T](xs: List[T],ys:List[T]): List[T]=
(xs foldLeft ys)(_::_)
实际上我很难理解foldRight和foldLeft之间的区别,有没有例子说明真正的差异?
感谢。
答案 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 = _ :: _
为我们提供正确的类型。
注意,foldl和foldr的参数是反转的,首先是函数,它们是签名中的初始参数r
,而不是::
的列表构造它只使用{{ 1}}。两个非常小的细节。