为什么不通过Nil到foldLeft工作?

Rus*_*ell 14 scala type-inference fold

当我使用我构建一个列表时,foldLeft经常会因为必须明确键入注入的参数而烦恼,并希望我可以使用"Nil" - 这是一个人为的例子:

scala> List(1,2,3).foldLeft(List[Int]())((x,y) =>  y :: x)
res17: List[Int] = List(3, 2, 1)

scala> List(1,2,3).foldLeft(Nil)((x, y) => y :: x)
<console>:10: error: type mismatch;
 found   : List[Int]
 required: scala.collection.immutable.Nil.type
              List(1,2,3).foldLeft(Nil)((x,y) =>  y :: x)
Run Code Online (Sandbox Code Playgroud)

这一点并不是很糟糕,List[Int]但只要你开始使用自己的类列表,几乎可以肯定会有更长的名称,甚至是元组或其他容器的列表,所以你需要指定多个类名,它变得可怕:

list.foldLeft(List.empty[(SomethingClass, SomethingElseClass)]) { (x,y) => y :: x }
Run Code Online (Sandbox Code Playgroud)

我猜它不起作用的原因是,虽然像5 :: Nil编译器这样的东西可以推断出空列表的类型List[Int],但是当Nil它作为参数传递给foldLeft它时没有足够的信息这样做,当它被使用时,它的类型被设置.但是 - 它真的不可能吗?难道它不能从作为第二个参数传递的函数的返回类型推断出类型吗?

如果没有,是否有一些我不知道的整洁成语?

kir*_*uku 14

Scalas类型推理引擎从左到右工作 - 因此scalac无法推断foldLeft的第一个参数列表的正确类型.您必须为编译器提供一个提示,使用哪种类型.而不是使用List[TYPE]()你可以使用List.empty[TYPE]:

(List(1,2,3) foldLeft List.empty[Int]) { (x,y) =>  y :: x }
(List.empty[Int] /: List(1, 2, 3)) { (x,y) => y :: x }
Run Code Online (Sandbox Code Playgroud)

  • @Russell:不,没有更短的路.您唯一能做的就是定义一个类型别名,当您多次出现"long"类型时,该别名非常有用. (3认同)

Rex*_*err 12

Scala的类型推断一次只能处理一个参数块. Nil没有任何其他上下文没有特定类型,因此选择最具限制性的类型possible(Nothing).

它也无法推断函数的返回类型,因为返回类型取决于类型Nil.一般来说,摆脱这种循环是很棘手的(如果你没有说明你的意思).

但是,您可以应用一些技巧来减少麻烦.

首先,类型签名fold只有集合的类型,所以如果你可以使用那种折叠,你可以解决问题.例如,您可以编写自己的反向展平:

List(List(1),List(2),List(3)).fold(Nil)( (x,y) => y ::: x )
Run Code Online (Sandbox Code Playgroud)

其次,如果您要创建相同类型的新集合,则使用现有集合生成空集合通常比尝试插入空集合的类型更容易.如果您在某处定义了管道,这将特别容易:

class PipeAnything[A](a: A) { def |>[B](f: A => B) = f(a) }
implicit def anything_can_be_piped[A](a: A) = new PipeAnything(a)

List(1,2,3) |> { x => x.foldLeft(x.take(0))( (y,z) => z :: y ) }
Run Code Online (Sandbox Code Playgroud)

最后,不要忘记您可以非常轻松地定义自己的方法,可以执行与您的类型一致的操作,即使您必须使用一些技巧来使其工作:

def foldMe[A,B](example: A, list: List[B])(f: (List[A],B) => List[A]) = {
  (List(example).take(0) /: list)(f)
}

scala> foldMe( ("",0), List("fish","wish","dish") )( (x,y) => (y.take(1), y.length) :: x )
res40: List[(java.lang.String, Int)] = List((d,4), (w,4), (f,4))
Run Code Online (Sandbox Code Playgroud)

在这里,请注意该示例不是零,而是仅用作具有所需类型的事物的示例.