有没有简洁的方法来"反转"一个选项?

Emi*_*erg 12 scala optional

假设我有一个可以接受可选参数的函数,Some如果参数是None,我想返回a,如果参数是,None则返回a Some:

def foo(a: Option[A]): Option[B] = a match {
  case Some(_) => None
  case None    => Some(makeB())
}
Run Code Online (Sandbox Code Playgroud)

所以我想要做的就是反过来map.变体orElse不适用,因为它们保留了a它存在的价值.

有没有更简洁的方法来做到这一点if (a.isDefined) None else Some(makeB())

Naz*_*iuk 8

fold比模式匹配更简洁

 val op:Option[B] = ...

 val inv = op.fold(Option(makeB()))(_ => None)
Run Code Online (Sandbox Code Playgroud)

  • 好主意,但如果我尝试编译它,它给出:错误:`对象没有不采用类型参数`.如果没有明确写出"选项",我无法解决这个问题? (3认同)

And*_*kin 6

这个答案概述:

  1. 单线解决方案使用 fold
  2. 与小的演示 fold
  3. 讨论为什么解决fold方案可能像解决方案一样"明显" if-else.

您可以随时使用fold转换Option[A]为您想要的任何内容:

a.fold(Option(makeB())){_ => Option.empty[B]}
Run Code Online (Sandbox Code Playgroud)

演示

这是一个完整的可运行示例,包含所有必需的类型定义:

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))
Run Code Online (Sandbox Code Playgroud)

这输出:

None
Some(Main$$anon$1$B@5fdef03a)
None
Some(Main$$anon$1$B@48cf768c)
Run Code Online (Sandbox Code Playgroud)

为什么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)
}
Run Code Online (Sandbox Code Playgroud)

可以像这样使用的语法:

val optStr: Option[String] = Some("hello")

val reversed = ifNone(optStr) { 
  Some("makeB") 
} otherwise {
  str => None
}
Run Code Online (Sandbox Code Playgroud)

而且,更重要的是,如果在过去半个世纪发明的每种编程语言的每一个介绍的第一页上都提到了这种语法,那么ifNone-otherwise解决方案(即fold)对于大多数人来说看起来更自然.

事实上,该Option.fold方法是消除了的Option[T]类型:每当我们有一个Option[T],并希望得到一个A出来,期待最明显的事情应该是fold(a)(b)a: Ab: T => A.相反与布尔值的特殊处理if-else-syntax(这仅仅是一种约定),该fold方法是非常基本的,事实上,它必须有能够从第一原理得出.