for..else 用于 Scala 中的选项类型?

问题描述 投票:0回答:9

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

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

除了

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

scala monads for-comprehension
9个回答
28
投票

通过使用

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

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

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

getOrElse
块。


13
投票

您可以同时对两个

Options
进行模式匹配:

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

6
投票

Scalaz 中的

traverse
函数概括了您的问题。它需要两个参数:

  1. T[F[A]]
  2. 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)

构建您想要收集结果的函数:

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

并致电

traverse

  • val r = z 遍历 f

这种编程模式经常出现。它有一篇论文对此进行了全面讨论,迭代器模式的本质

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


4
投票

为什么这样的东西不起作用?

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
}

3
投票

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

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

2
投票

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

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

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

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


2
投票

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" }

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>

0
投票

我认为这里的关键点是根据你想要做的类型来思考。据我了解,您想要迭代选项对列表,然后根据特定条件执行某些操作。

所以你的问题中有趣的一点是,除了什么之外,返回类型会是什么样子?我认为它看起来像这样:

Either[List[Option], List [Option,Option]]

在错误侧(左),您将累积与 None 配对的选项(可以说是单独留下的)。在右侧,您可以总结代表您成功价值观的非空选项。所以我们只需要一个能做到这一点的函数。验证每一对并根据其结果(成功 - 失败)进行累加。

实现我所描述内容的一些链接:

© www.soinside.com 2019 - 2024. All rights reserved.