用于替代if/else/else链的选项的惯用Scala

Dav*_*les 5 coding-style scala optional

我经常发现自己写的形式为Scala:

def foo = {
  f1() match {
    case Some(x1) => x1
    case _ =>
      f2() match {
        case Some(x2) => x2
        case _ =>
          f3() match {
            case Some(x3) => x3
            case _ =>
              f4()
          }
      }
  }
}
Run Code Online (Sandbox Code Playgroud)

这是Java的道德等价物

Object foo() {
    Object result = f1();
    if (result != null) {
        return result;
    } else {
        result = f2();
        if (result != null) {
            return result;
        } else {
            result = f3();
            if (result != null) {
                return result;
            } else {
                return f4();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它似乎很丑陋,而且不必要地冗长.我觉得应该有一种可读的方法在Scala的一行中做到这一点,但我不清楚它是什么.

注意:我查看了Idiomatic Sc​​ala的嵌套选项,但它的情况有所不同.

Reg*_*anc 8

写嵌套图案与在Scala中选择匹配的惯用方法是通过使用这些方法map,flatMap,orElse,和getOrElse.

map希望进一步处理选项内容并保留可选行为时使用:

所以不是这样的:

val opt: Option[Int] = ???
opt match {
  case Some(x) => Some(x + 1)
  case None => None
}
Run Code Online (Sandbox Code Playgroud)

你会这样做:

val opt: Option[Int] = ???
opt.map(_ + 1)
Run Code Online (Sandbox Code Playgroud)

这更自然地链接:

opt.map(_ + 1).map(_ * 3).map(_ - 2)
Run Code Online (Sandbox Code Playgroud)

flatMap 完全相似,但在进一步操作返回选项类型时使用.

在您的特定示例中,orElse似乎是最适合的解决方案.orElse如果不为空,您可以使用返回选项本身,或者返回参数.请注意,该参数是延迟评估的,因此它实际上等同于嵌套模式匹配/ if-then-else.

def foo = {
  f1().orElse(f2())
      .orElse(f3())
      .orElse(f4())
}
Run Code Online (Sandbox Code Playgroud)

getOrElse如果您想要删除选项,也可以将它们组合在一起.在您的示例中,您似乎返回最终f4,就好像它没有返回Option,所以您将执行以下操作:

def foo = {
  f1().orElse(f2())
      .orElse(f3())
      .getOrElse(f4())
}
Run Code Online (Sandbox Code Playgroud)


net*_*kin 8

我知道我迟到了,但觉得orElse这里的解决方案有点笨拙.对我来说,一般的功能习语(不仅仅是scalaic)会是...... 沿着这些路线(原谅我,我不是斯卡拉精通):

def f1 = () => { println("f1 here"); null }
def f2 = () => { println("f2 here"); null }
def f3 = () => { println("f3 here"); 2 }
def f4 = () => { println("f4 here"); 3 }
def f5 = () => { println("f5 here"); 43 }

Stream(f1, f2, f3, f4, f5)
  .map(f => f())
  .dropWhile(_ == null)
  .head
Run Code Online (Sandbox Code Playgroud)

你使用Stream作为一个惰性列表,基本上,你说:开始调用函数并给我第一个不评估为零的函数.结合声明性方法和懒惰为您提供了这个通用的代码片段,其中当函数数量发生变化时,您唯一需要更改的是输入列表(流),只需添加一个函数元素.这样,函数f1 ... fn成为数据,因此您不必修改任何现有代码.


cmb*_*ter 5

你可以尝试:

f1() orElse f2() orElse Option(f3()) getOrElse f4()
Run Code Online (Sandbox Code Playgroud)

假设f1和f2返回相同类型的选项,f3和f4返回相同类型的非选项

编辑

你的例子中是否完全清楚是否f3()返回Option,所以如果它返回,那么代码将被简化为:

f1() orElse f2() orElse f3() getOrElse f4()
Run Code Online (Sandbox Code Playgroud)