假设我有一个可以接受可选参数的函数,我想返回一个 Some
如果论证是 None
和a None
如果论证是 Some
:
def foo(a: Option[A]): Option[B] = a match {
case Some(_) => None
case None => Some(makeB())
}
所以我想要做的就是反过来 map
。的变种 orElse
不适用,因为它们保留了价值 a
如果它存在。
有没有更简洁的方法来做到这一点 if (a.isDefined) None else Some(makeB())
?
这个答案概述:
- 单线解决方案使用
fold
- 与小的演示
fold
- 讨论为什么
fold
-解 可以 和...一样“明显” if-else
-解。
解
你可以随时使用 fold
改造 Option[A]
你想要的任何东西:
a.fold(Option(makeB())){_ => Option.empty[B]}
演示
这是一个完整的可运行示例,包含所有必需的类型定义:
class A
class B
def makeB(): B = new B
def foo(a: Option[A]): Option[B] = a match {
case Some(_) => None
case None => Some(makeB())
}
def foo2(a: Option[A]): Option[B] =
a.fold(Option(makeB())){_ => Option.empty[B]}
println(foo(Some(new A)))
println(foo(None))
println(foo2(Some(new A)))
println(foo2(None))
这输出:
None
Some(Main$$anon$1$B@5fdef03a)
None
Some(Main$$anon$1$B@48cf768c)
为什么 fold
只要 似乎 不太直观
在评论中,@ TheArchetypalPaul评论说 fold
似乎“不那么明显”了 if-else
解。
我会声称这主要是因特殊存在而产生的神器 if-else
布尔语的语法。
如果有像标准的东西
def ifNone[A, B](opt: Option[A])(e: => B) = new {
def otherwise[C >: B](f: A => C): C = opt.fold((e: C))(f)
}
可以像这样使用的语法:
val optStr: Option[String] = Some("hello")
val reversed = ifNone(optStr) {
Some("makeB")
} otherwise {
str => None
}
更重要的是,如果在过去半个世纪发明的每种编程语言的每一个介绍的第一页上都提到了这种语法,那么 ifNone-otherwise
解决方案(即 fold
),对大多数人来说看起来会更自然。
的确如此 Option.fold
方法是 消除 的 Option[T]
类型:每当我们有一个 Option[T]
并希望得到一个 A
出于它,最明显的期望应该是a fold(a)(b)
同 a: A
和 b: T => A
。与布尔人的特殊待遇相比 if-else
-syntax(这仅仅是一种惯例), fold
方法很基础,事实就是如此 一定是在那里 可以源于第一原则。
fold比模式匹配更简洁
val op:Option[B] = ...
val inv = op.fold(Option(makeB()))(_ => None)
我想出了这个定义 a.map(_ => None).getOrElse(Some(makeB()))
:
scala> def f[A](a: Option[A]) = a.map(_ => None).getOrElse(Some(makeB()))
f: [A](a: Option[A])Option[makeB]
scala> f(Some(44))
res104: Option[makeB] = None
scala> f(None)
res105: Option[makeB] = Some(makeB())