【问题标题】:Folding over a list of Options to Find First or Last Some折叠选项列表以查找第一个或最后一个
【发布时间】:2011-12-23 18:23:59
【问题描述】:

我正在尝试折叠选项列表,以便返回第一个(或最后一个)Some 值,如果没有 Some 值,则返回 None。

scala> val opts = List(None, Some(1), None, Some(2), None)
opts: List[Option[Int]] = List(None, Some(1), None, Some(2), None)

scala> opts foldLeft(None)((a,io) => a match { case None => io; case Some(i) =>
a})
<console>:9: error: object None does not take parameters
              opts foldLeft(None)((a,io) => a match { case None => io; case Some
(i) => a})
                                 ^

不知道我做错了什么。也可能有一种方法可以使用高阶函数更简单地完成此操作,但 here 中的任何内容都没有引起我的注意。

【问题讨论】:

    标签: scala folding


    【解决方案1】:

    也许这可以解决你的问题——第一个元素:

    opts.flatten.headOption
    

    最后一个元素:

    opts.flatten.lastOption
    

    flatten 方法将取消列表中的所有Option 值并删除所有None 值。 headOption/lastOption 将返回Some 作为列表中的第一个/最后一个元素,如果列表为空,则返回None

    【讨论】:

      【解决方案2】:

      tenshi’s answer 非常简单,但对于长列表,它会尝试扁平化所有内容,因为它并不懒惰。 (我认为view 在这里也帮不了我们,但我不太确定。)

      在这种情况下,您可以使用:

      opts.dropWhile(_.isEmpty).headOption.flatMap(identity)
      

      不幸的是,我们不能在这里使用flatten,因为这将返回一个通用的Iterable[Int],而不是Option,所以我们必须选择更长的成语flatMap(identity)

      编辑:dave 注意到:

      opts.find(_.isDefined).flatMap(identity)
      

      会更好。

      【讨论】:

      • 似乎 find 返回它找到的第一个。有没有办法找到列表中的第 n 个或最后一个?
      • 我认为对于最后一个,您必须先反转列表。
      • opts.dropWhile(_.isEmpty).headOption 返回Option[Option[Int]],对吧?!但是为什么那个双重选项上的flatten 不返回Option[Int]ScalaDoc-Option 声明 ev: &lt;:&lt;[A, Option[B]] 在我们的例子中它应该与 A = Option[Int] 一起使用。
      • @PeterSchmitz:ScalaDoc 所指的夜间构建是正确的。这确实会在 2.10 中得到修复(当然,除非他们出于某种原因恢复了更改)。另一方面,在 Scala 2.9 中,Option 上没有方法 flatten - 只有在这种情况下执行到 Iterable 的隐式转换。
      • @Debilski 啊,太好了。在查看夜间文档时,我很困惑,因为我的代码 (2.9.1) 做出了超出预期的反应。当然应该采取适当的文档;)
      【解决方案3】:

      有更好的方法,但要回答提出的问题,您有两个问题

      1) 您在opts 后面缺少.。您只能使用中缀表示法将a.m(b) 转换为a m b。 foldLeft 方法的格式为a.m(b)(c)。所以要么这样写,要么包括括号(a m b)(c)

      2) 您需要将None 参数化为Option[Int]:这里将其解释为None 对象,而不是Option[Int] 实例的值。

      所以这会起作用:

      opts.foldLeft(None: Option[Int])(
        (a,io) => a match { case None => io; case Some(i) => a } )
      

      【讨论】:

        【解决方案4】:

        为什么要这么麻烦?

        opts.find(_.nonEmpty).flatten
        opts.reverse.find(_.nonEmpty).flatten
        

        【讨论】:

        • 因为我还在学习scala ...还有没有办法找到第n个值?
        • @Trevor - opts.view.filter(_.nonEmpty).drop(n-1).headOption.flatten 是一种无需展平整个列表即可找到 nth 项的方法。
        • 可能应该指出flatten 将返回List,而不是像其他答案那样返回Option
        • @LuigiPlinge 你的意思是在“nth”值的情况下?是的,那个例子没有用。它会抛出异常,并返回List。我删除了它。但答案中的代码按预期返回Option
        • 嗯,对我来说不是,在 2.9.0 中:它返回 Iterable[Int] = List(2)。从 Debilski 在他的问题中的回答来看,也许这是 2.9 和 2.10 之间的区别。
        【解决方案5】:

        Scala 2.9开始,可以使用collectFirst提取第一个定义的选项:

        List(None, Some(1), None, Some(2), None).collectFirst { case Some(x) => x }
        // Option[Int] = Some(1)
        

        这将在第一个定义的Option 处停止迭代,并避免在寻找头部之前将整个List 展平。


        Scala 2.13开始你可以使用findLast,这是find的反面(同时等待可能的collectLast?)

        List(None, Some(1), None, Some(2), None).findLast(_.isDefined).flatten
        // Option[Int] = Some(2)
        

        【讨论】:

          猜你喜欢
          • 2019-08-12
          • 2011-09-25
          • 1970-01-01
          • 2019-12-09
          • 2022-12-18
          • 2021-01-07
          • 1970-01-01
          • 2018-08-15
          • 1970-01-01
          相关资源
          最近更新 更多