将无折叠集合作为起始值

时间:2014-07-31 18:58:53

标签: scala collections option fold

我想折叠一个集合或Y'并返回一个选项[X]。我想从无。开始。像这样......

def f(optX: Option[X], y: Y): Option[X]
val optX = collectionOfY.fold(None) { case (prev, y) => f(prev,y) }

添加不需要的类型以使其更清晰

val optX: Option[X] = collectionOfY.fold(None) { case (prev: Option[X], y: Y) => f(prev,y) }

但是,编译器无法正确识别类型,我必须像这样编写

val xx: Option[X] = None
val optX = collectionOfY.fold(xx) { case (prev, y) => f(prev,y) }

写这篇文章的神奇Scala语法是什么?

由于 彼得

3 个答案:

答案 0 :(得分:5)

只需使用foldLeft以及以下任何内容

即可

... foldLeft(Option.empty[X]) ...... foldLeft(None: Option[X]) ...... foldLeft[Option[X]](None) ...

毕竟,fold只是致电foldLeft。当fold确实是A1的超类型时,您真的只想使用A,如果情况确实如此,那么您可以使用fold,如上所述编译器会正确地知道类型。

例如,Option[List[Int]] <: Option[Seq[Int]]为协方差,因此我们在此处未获得Any

List(Some(List(1,2,3))).fold[Option[Seq[Int]]](None)((_, _) => Some(Seq(1)))

> res2: Option[Seq[Int]] = Some(List(1))

最后,如果您确实知道Option[X]将是Y的超类型,那么在Y的类型声明中明确说明 - 即Y <: Option[X],那么您可以将fold与上面给出的解决方案一起使用。

有关相关讨论,请参阅When should .empty be used versus the singleton empty instance?

答案 1 :(得分:1)

此行为是合乎逻辑的,因为fold定义为

def fold[A1 >: A](z: A1)(op: (A1, A1) ⇒ A1): A1

这意味着起始参数是A的超类型,但NoneOption[T]的超类型。如果直接指定类型(如第二个示例中所示),则编译器有足够的信息来确定折叠的返回类型。

一种可能的解决方法是指定集合头部与None的交互结果作为起点,并将其与集合的其余部分折叠(添加Nil的检查):

val optX = collectionOfY.match {
  case Nil => None
  case x:xs => xs.fold(f(None,x)) {case (prev,y) => f(prev,y) }
}

答案 2 :(得分:1)

正如上面的评论所指出的,首选解决方案是更改传递给Option.fold的第一个参数,以避免使用None并改为使用Option.empty[X]

val optX = collectionOfY.fold(Option.empty[X]) { case (prev, y) => f(prev,y) }

Scala编译器毫无怨言地接受了这一点。