我想知道为什么scala.Option
没有这样定义的方法fold
:
fold(ifSome: A => B , ifNone: => B)
相当于
map(ifSome).getOrElse(ifNone)
没有比使用map
+ getOrElse
更好的了吗?
答案 0 :(得分:67)
我个人发现像cata
这样的方法需要两个闭包,因为参数往往过度。你真的在map
+ getOrElse
上获得了可读性吗?想想你的代码的新手:他们会做什么
opt cata { x => x + 1, 0 }
你真的认为这比
更清楚opt map { x => x + 1 } getOrElse 0
事实上,我认为两者都不比优秀的旧
更好opt match {
case Some(x) => x + 1
case None => 0
}
与往常一样,有一个限制,即额外的抽象不会给你带来好处并且会产生适得其反的效果。
答案 1 :(得分:41)
最后添加了in Scala 2.10,签名为fold[B](ifEmpty: => B)(f: A => B): B
。
不幸的是,这有一个共同的负面后果:B
仅针对基于ifEmpty
参数的调用推断,实际上这通常更为狭窄。例如。 (正确的版本已经在标准库中,这仅用于演示)
def toList[A](x: Option[A]) = x.fold(Nil)(_ :: Nil)
Scala会将B
推断为Nil.type
而不是期望的List[A]
,并抱怨f
没有返回Nil.type
。相反,你需要一个
x.fold[List[A]](Nil)(_ :: Nil)
x.fold(Nil: List[A])(_ :: Nil)
这使fold
与相应的match
不完全相同。
答案 2 :(得分:25)
你可以这样做:
opt foldLeft (els) ((x, y) => fun(x))
或
(els /: opt) ((x,y) => fun(x))
(两个解决方案都会按值评估 els
,这可能不是您想要的。感谢Rex Kerr指向它。)
修改强>
但你真正想要的是Scalaz的catamorphism cata
(基本上是fold
,它不仅处理Some
值,还会映射None
部分,这就是你所描述的)
opt.cata(fun, els)
定义为(其中value
是拉皮条选项值)
def cata[X](some: A => X, none: => X): X = value match {
case None => none
case Some(a) => some(a)
}
相当于opt.map(some).getOrElse(none)
。
虽然我应该说,只有在表达它的“更自然”的方式时才应该使用cata。在很多情况下,简单的map
- getOrElse
就足够了,特别是当它涉及潜在链接大量map
时。 (虽然你也可以用函数组合来链接fun
,当然 - 这取决于你是否要关注函数组合或值转换。)
答案 3 :(得分:19)
如Debilski所述,您可以使用Scalaz的OptionW.cata
或fold
。正如Jason评论的那样,命名参数使它看起来很漂亮:
opt.fold { ifSome = _ + 1, ifNone = 0 }
现在,如果某个None
mzero
案例中您想要的值为Monoid[M]
,而f: A => M
案例的Some
函数为opt foldMap f
,你可以这样做:
opt map (_ + 1) getOrElse 0
所以,
opt foldMap (_ + 1)
变为
Option
就我个人而言,我认为apply
应该有一个opt { _ + 1, 0 }
方法,这将是一种变形。这样你就可以这样做:
opt { some = _ + 1, none = 0 }
或
{{1}}
事实上,对于所有代数数据结构来说,这将是很好的。