假设我有两个选项,如果两者都是 Some,则执行一个代码路径,如果注意,则执行另一个。我想做类似的事情
for (x <- xMaybe; y <- yMaybe) {
// do something
}
else {
// either x or y were None, handle this
}
在if
语句或模式匹配之外(如果我有两个以上的选项,可能无法扩展),有没有更好的方法来处理这个?
假设我有两个选项,如果两者都是 Some,则执行一个代码路径,如果注意,则执行另一个。我想做类似的事情
for (x <- xMaybe; y <- yMaybe) {
// do something
}
else {
// either x or y were None, handle this
}
在if
语句或模式匹配之外(如果我有两个以上的选项,可能无法扩展),有没有更好的方法来处理这个?
yield
通过使用将for
输出包装在选项中,非常接近您的语法建议:
val result = {
for (x <- xMaybe; y <- yMaybe) yield {
// do something
}
} getOrElse {
// either x or y were None, handle this
}
仅当一个或两个选项为 None 时才执行该getOrElse
块。
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"
}
Scalaz 中的traverse
函数在这里概括了您的问题。它需要两个参数:
T[F[A]]
A => F[B]
并返回F[T[B]]
。是任何可遍历的数据结构,T
例如是任何应用函子,例如. 因此,为了专业化,您所需的功能具有以下类型:List
F
Option
List[Option[A]] => (A => Option[B]) => Option[List[B]]
所以把你所有的Option
价值观都放在一个List
val z = List(xMaybe, yMaybe)
构造函数得到但是你想收集结果:
并打电话traverse
这种编程模式经常出现。它有一篇关于它的论文,迭代器模式的本质。
注意:我只是想修复 URL,但 CLEVER 编辑帮助告诉我至少需要更改 6 个字符,所以我也包含了这个有用的链接(scala 示例):
http ://etorreborre.blogspot.com/2011/06/迭代器模式的本质.html
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
}
如果您不知道要处理的值的数量,那么 Tony 的答案是最好的。如果您确实知道要处理的值的数量,那么我建议您使用应用函子。
((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)
您说您希望解决方案具有可扩展性:
val optional = List(Some(4), Some(3), None)
if(optional forall {_.isDefined}) {
//All defined
} else {
//At least one not defined
}
编辑:刚刚看到Emil Ivanov的解决方案更加优雅。
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>
我认为这里的关键点是根据类型来思考你想要做什么。据我了解,您想遍历选项对列表,然后根据特定条件执行某些操作。所以你的问题中有趣的一点是,除了返回类型之外,返回类型是什么样的?我认为它看起来像这样: 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 -桑吉夫/
开始,如果两个选项都被定义或者没有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" }