Scala中嵌套的"if"/"match"语句:更好的方法?

4
我有一系列验证函数,如果有任何问题,这些函数将返回Option[Problem],否则返回None。 我想编写一个简单的函数,调用每个验证函数,停止并返回第一个非None结果。
当然,我可以按“Java风格”编写此方法,但我想知道是否存在更好的方法。
编辑:
以下是原始的Java解决方案:
validate01(arg);
validate02(arg);
validate03(arg);
...

每个方法在出现问题时都会抛出异常。在编写Scala时,我建议避免使用异常。

“Better”是什么意思?“Java-style”是什么? - Robert Harvey
3
你能提供你想要改进的代码示例吗? - om-nom-nom
有人请在Scala中添加单子! - Dmytro Sirenko
@RobertHarvey “Java风格”意味着冗长的方法“if/else if/else if…”(或类似使用match)。 - Marco
3个回答

6

举例来说,假设我们想要验证一个 String。我们的验证函数接受一个 String 和一个验证器列表,这些验证器是从 StringOption[Problem] 的函数。我们可以按照函数式编程的方式实现它:

def firstProblem(validators: List[String => Option[Problem]], s:String) =
  validators.view.flatMap(_(s)).headOption

通过将每个验证函数应用于字符串并仅在结果为Some时保留该结果,可以创建一个新的列表。然后我们取这个列表的第一个元素。由于调用了view,因此只有在需要时才会计算列表。因此,一旦找到第一个问题,就不会再调用更多的验证器。


你也可以这样写 - def firstProblem(validators: List[String => Option[Problem]], s: String) = { var r: Option[Problem] = None; for(v <- validators) r = r.orElse(v(s)); r.get } - Tesseract
1
@SpiderPig:你可以这样做,但他是在寻找一个功能性的解决方案。 - Kim Stebel
@KimStebel 是的,你说得对,我正在寻求一个功能性的解决方案。 - Marco

6

如果您在编译时知道有限数量的验证,可以在选项上使用.orElse:

def foo(x: Int): Option[Problem] = ...
def bar(x: Int): Option[Problem] = ...
...
def baz(x: Int): Option[Problem] = ...


foo(1) orElse bar(2) orElse .. baz(n)

2
您还可以使用foldLeft将orElse应用于任意Seq的验证; 类似于vs.foldLeft(Option.empty[Problem]) { (result, validation) => result orElse validation(n) } - Greg Campbell
@GregCampbell:然后你有我的解决方案,只是更复杂而且不懒惰。 - Kim Stebel

1

也许你想要——假设验证函数不需要参数

def firstProblem(fs: (() => Option[Problem])*) = {
  fs.iterator.map(f => f()).find(_.isDefined).flatten
}

如果存在问题,您将获得一个现有的Option[Problem],如果全部成功,则为None。如果您需要向函数传递参数,则需要解释这些参数是什么。例如,您可以

def firstProblem[A](a: A)(fs: (A => Option[Problem])*) = /* TODO */

如果你可以将相同的参数传递给它们所有,你可以像这样使用它:

firstProblem(myData)(
  validatorA,
  validatorB,
  validatorC,
  validatorD
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接