我想折叠一个集合或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语法是什么?
由于 彼得
答案 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
的超类型,但None
是Option[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编译器毫无怨言地接受了这一点。