迭代 Option 实例,直到找到第一个非空

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

我有许多返回

Option
值的函数,就像这样

case class A()
case class B() 
case class C()

def optionA(): Option[A] = None
def optionB(): Option[B] = Some(B())
def optionC(): Option[C] = Some(C())

我想要做的是,我想按顺序运行这些函数,但直到其中一个函数返回带有值(a

Option
)的
Some
。然后我想返回该值,而不运行其余的函数。

这是我当前的实现

val res:Option[Any] = Stream(
  () => optionA(),
  () => optionB(),
  () => optionC()
) .map(f => f())
  .filter(opt => opt.isDefined)
  .head

对于上面的函数实现,这适用于

optionA
optionB
,给我一个
Some(B())
,并且它永远不会运行
optionC
,这就是我想要的。

但我想知道是否有更好/简单/替代的实现。

类似

val findFirst = optionA compose optionB compose optionC
的东西?

scala functional-programming
3个回答
8
投票
optionA().orElse(optionB()).orElse(optionC())
如果定义了

orElse

,则 
this
将不会计算其参数。

或者,如果您已经在集合/流中拥有选项,您可以这样做

options.find(_.isDefined).flatten

3
投票

假设您现在拥有

Option
的集合,那么您可以执行以下操作:

coll.foldLeft[Option[Int]](None)(_ orElse _)

这将返回集合中第一个非

None

请注意,我明确提到了集合的类型,因为 scala 无法推断

orElse
在没有它的情况下应该做什么...(
None
默认情况下是
Option[Any]
类型)

如果你有一个巨大的选项列表,写下来可能会有帮助

coll.view.foldLeft[Option[Int]](None)(_ orElse _)

0
投票

有几件事需要指出。

  1. 无需使用
    isDefined
    ,如已接受的答案所示。事实上,显式检查
    Option
    是否为空是一种反模式,因为它与检查
    null
    没有什么不同,而
    Option
    的存在正是为了避免这种情况。
Seq(None, Some(1), None, Some(2))
  .flatten
  .headOption // Some(1)
  1. 如果您已经有
    List
    Option
    ,或任何长度已知的集合,则上述方法没问题。但是,如果您想通过调用另一个函数来找到第一个非空结果,那么您需要注意一个陷阱。
def loop(i: Int): Option[String] =
  println(i)
  if i == 5 then Some("yes") else None

Iterator
  .range(0, 10)
  .flatMap(loop)
  .nextOption() // Some("yes")

这将打印

0
5
。然而:

(0 to 9)
  .flatMap(loop)
  .headOption // Some("yes")

将打印

0
9
。即使值
5
返回非空
Option
,迭代也会继续,直到集合耗尽。

上面的代码是用 Scala 3.3.4 测试的。

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