17

假设我有两个选项,如果两者都是 Some,则执行一个代码路径,如果注意,则执行另一个。我想做类似的事情

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

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

4

9 回答 9

26

yield通过使用将for输出包装在选项中,非常接近您的语法建议:

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

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

于 2011-07-31T19:13:46.667 回答
13

You could pattern match both Options at the same time:

(xMaybe, yMaybe) match {
  case (Some(x), Some(y)) => "x and y are there"
  case _ => "x and/or y were None"
}
于 2011-07-31T17:57:15.967 回答
7

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

  1. T[F[A]]
  2. A => F[B]

并返回F[T[B]]。是任何可遍历的数据结构,T例如是任何应用函子,例如. 因此,为了专业化,您所需的功能具有以下类型:ListFOption

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

所以把你所有的Option价值观都放在一个List

  • val z = List(xMaybe, yMaybe)

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

  • val f: X => 选项[Y] = ...

并打电话traverse

  • val r = z 遍历 f

这种编程模式经常出现。它有一篇关于它的论文,迭代器模式的本质

注意:我只是想修复 URL,但 CLEVER 编辑帮助告诉我至少需要更改 6 个字符,所以我也包含了这个有用的链接(scala 示例):
http ://etorreborre.blogspot.com/2011/06/迭代器模式的本质.html

于 2011-07-31T21:10:57.680 回答
5

Why would something like this not work?

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
}
于 2011-07-31T17:57:48.313 回答
4

如果您不知道要处理的值的数量,那么 Tony 的答案是最好的。如果您确实知道要处理的值的数量,那么我建议您使用应用函子。

((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)
于 2011-07-31T21:30:08.003 回答
3

您说您希望解决方案具有可扩展性

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

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

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

于 2011-07-31T18:15:11.430 回答
0

For scaling to many options, try something along these lines:

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

With this, you can do:

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>
于 2011-07-31T17:59:06.193 回答
0

我认为这里的关键点是根据类型来思考你想要做什么。据我了解,您想遍历选项对列表,然后根据特定条件执行某些操作。所以你的问题中有趣的一点是,除了返回类型之外,返回类型是什么样的?我认为它看起来像这样: Either[List[Option], List [Option,Option]] 。在错误一侧(左),您将累积与 None 配对的选项(可以这么说)。在右侧,您将代表您成功值的非空选项相加。所以我们只需要一个能做到这一点的函数。验证每一对并根据其结果(成功 - 失败)累积。我希望这会有所帮助,如果没有,请更详细地解释您的用例。http://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/pdf/index.pdf和:http ://blog.tmorris.net/automated-validation-with-applicatives-and-semigroups-for -桑吉夫/

于 2011-08-01T08:18:23.073 回答
0

开始,如果两个选项都被定义或者没有Scala 2.13,我们也可以使用which 将两个选项连接到它们的值的一些元组: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" }
于 2019-06-08T07:00:53.897 回答