for..else Scala中的Option类型?

时间:2011-07-31 17:48:26

标签: scala monads for-comprehension

假设我有两个选项,如果两个都是Some,则执行一个代码路径,如果是note,则执行另一个。我想做一些像

这样的事情
for (x <- xMaybe; y <- yMaybe) {
  // do something
}
else {
  // either x or y were None, handle this
}

if语句或模式匹配之外(如果我有两个以上选项可能无法扩展),有没有更好的方法来处理它?<​​/ p>

9 个答案:

答案 0 :(得分:26)

使用yieldfor输出封装在选项中,非常接近您的语法提案:

val result = { 
  for (x <- xMaybe; y <- yMaybe) yield {
    // do something
  }
} getOrElse {
  // either x or y were None, handle this
}

仅当一个或两个选项均为None时,才会执行getOrElse块。

答案 1 :(得分:13)

您可以同时模式匹配Options

(xMaybe, yMaybe) match {
  case (Some(x), Some(y)) => "x and y are there"
  case _ => "x and/or y were None"
}

答案 2 :(得分:7)

Scalaz中的traverse函数在这里概括了您的问题。它需要两个参数:

  1. T[F[A]]
  2. A => F[B]
  3. 并返回F[T[B]]T是任何可遍历的数据结构,例如ListF是任何应用函数,例如Option。因此,要专业化,您所需的功能具有以下类型:

    • List[Option[A]] => (A => Option[B]) => Option[List[B]]

    将所有Option值放在List

    • val z = List(xMaybe, yMaybe)

    构造函数get但是你想收集结果:

    • val f:X =&gt;选项[Y] = ...

    并致电traverse

    • val r = z traverse f

    这种编程模式经常发生。它有一篇论文讲述了所有相关内容,The Essence of the Iterator Pattern

    注意:我只想修复网址,但是CLEVER编辑帮助告诉我我需要更改至少6个字符,所以我也包含了这个有用的链接(scala示例):
    http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html

答案 3 :(得分:5)

为什么这样的事情不起作用?

val opts = List[Option[Int]](Some(1), None, Some(2))
if (opts contains None) {
  // Has a None
} else {
  // Launch the missiles
  val values = opts.map(_.get) // We know that there is no None in the list so get will not throw
}

答案 4 :(得分:4)

如果您不知道您正在处理的值的数量,那么Tony的答案是最好的。如果你确实知道你正在处理的值的数量,那么我建议使用一个applicative functor。

((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)

答案 5 :(得分:3)

您说您希望解决方案可扩展

val optional = List(Some(4), Some(3), None)

if(optional forall {_.isDefined}) {
    //All defined
} else {
    //At least one not defined
}

编辑:刚看到 Emil Ivanov 的解决方案更优雅。

答案 6 :(得分:0)

要扩展到许多选项,请尝试以下几点:

 def runIfAllSome[A](func:(A)=>Unit, opts:Option[A]*) = {
   if(opts.find((o)=>o==None) == None) for(opt<-opts) func(opt.get)
 }

有了这个,你可以这样做:

scala> def fun(i:Int) = println(i)
fun: (i: Int)Unit

scala> runIfAllSome(fun, Some(1), Some(2))
1
2

scala> runIfAllSome(fun, None, Some(1))

scala>

答案 7 :(得分:0)

我认为这里的关键点是从类型的角度思考你想做什么。据我所知,你想迭代一个Option对列表,然后根据某个条件做一些事情。所以问题的有趣之处在于,返回类型会是什么样子,除此之外?我认为它看起来像这样:[List [Option],List [Option,Option]]。在错误方面(左侧),您将累积与无配对的选项(并且可以单独留下)。在右侧,您可以汇总代表您的成功值的非空选项。所以我们只需要一个完全正确的功能。验证每对并根据其结果累积(成功 - 失败)。我希望这有帮助,如果没有请更详细地解释你的用例。一些链接用于实现我所描述的内容:http://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/pdf/index.pdf和:http://blog.tmorris.net/automated-validation-with-applicatives-and-semigroups-for-sanjiv/

答案 8 :(得分:0)

Scala 2.13开始,我们也可以使用Option#zip将两个选项连接到它们的值的某些元组(如果两个选项均已定义,否则为无):

opt1 zip opt2 match {
  case Some((x, y)) => "x and y are there"
  case None         => "x and/or y were None"
}

或使用Option#fold

(opt1 zip opt2).fold("x and/or y were None"){ case (x, y) => "x and y are there" }