假设我有两个Options,如果都是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
块。
您可以同时对两个
Options
进行模式匹配:
(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/essence-of-iterator-pattern.html
为什么这样的东西不起作用?
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
}
如果您不知道正在处理的值的数量,那么托尼的答案是最好的。 如果您确实知道要处理的值的数量,那么我建议使用应用函子。
((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的解决方案更优雅一点。
从
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" }
要扩展到许多选项,请尝试以下操作:
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>
我认为这里的关键点是根据你想要做的类型来思考。据我了解,您想要迭代选项对列表,然后根据特定条件执行某些操作。
所以你的问题中有趣的一点是,除了什么之外,返回类型会是什么样子?我认为它看起来像这样:
Either[List[Option], List [Option,Option]]
。
在错误侧(左),您将累积与 None 配对的选项(可以说是单独留下的)。在右侧,您可以总结代表您成功价值观的非空选项。所以我们只需要一个能做到这一点的函数。验证每一对并根据其结果(成功 - 失败)进行累加。
实现我所描述内容的一些链接: