【问题标题】:Converting List[Option[A]] to Option[List[A]] using Map instead of FlatMap使用 Map 而不是 FlatMap 将 List[Option[A]] 转换为 Option[List[A]]
【发布时间】:2013-06-24 05:03:52
【问题描述】:

Functional Programming in Scala 书中有一个问题要求将选项列表转换为列表选项。函数签名如下:

def sequence[A](a:List[Option[A]]):Option[List[A]]

在书的网站上,这个功能是这样实现的:

def sequence[A](a:List[Option[A]]):Option[List[A]] = a match {
   case Nil => Some(Nil)
   case h::t => h flatMap (r => sequence(t) map (h::_))
}

我对@9​​87654324@ 部分感到有些困惑。如果我把它分解,它是这样的:

h 的类型为 Option[A]。在 h 上执行 flatMap 将返回一个 Option[B],但它以一个函数 f 作为参数,该函数以 A 作为参数并返回一个 Option[B],现在在上面的示例中,sequence(t) map (h :: _) 将返回一个 Option[List[A]],它与函数的返回类型。

可以使用 map 代替 flatMap 来执行从 List[Option[A]]Option[List[A]] 的转换吗?此外,提供的解决方案似乎不是尾递归的。可以做尾递归吗?

【问题讨论】:

    标签: list scala map higher-order-functions


    【解决方案1】:

    有错别字:

    case h::t => h flatMap (r => sequence(t) map (h::_))
    

    应该有r :: _,或者h.toList ::: _,而不是h :: _

    sequence(t) 返回Option[List[A]]map (r::_) on Option[List[A]] 不要更改类型。它只需要来自OptionList[A](如果有),并在其前面加上A

    所以sequence(t) map (r :: _) 的类型是Option[List[A]]

    这里不需要flatMap

    def sequence[A](a:List[Option[A]]):Option[List[A]] = a match {
       case Nil => Some(Nil)
       case None :: _ => None
       case Some(r) :: t => sequence(t) map (r :: _)
    }
    

    解决方案可以是尾递归的。尾递归和List 的常见问题是你必须在最后反转你的列表:

    def sequence[A](a:List[Option[A]]):Option[List[A]] = {
      @tailrec def loop(a: List[Option[A]], subres: List[A] = Nil): Option[List[A]] =
        a match {
          case Nil => Some(subres)
          case None :: _ => None
          case Some(r) :: t => loop(t, r :: subres)
        }
      loop(a) map {_.reverse}
    }
    

    事实上它完全可以不递归:

    def sequence[A](a:List[Option[A]]):Option[List[A]] =
      a.foldLeft(Option(List[A]())){ (os, oe) =>
        for {
          s <- os
          e <- oe
        } yield e :: s
      }.map{ _.reverse }
    

    【讨论】:

    • 第二种解决方案不适用于sequence(List(Some(1), Some(2),None))。它返回None
    【解决方案2】:

    h flatMap (r =&gt; sequence(t) map (h::_)) 应该是 h flatMap (r =&gt; sequence(t) map (r::_)),因为 hOption[A] 类型,rA 类型。我们正在尝试将元素附加到地图中 List[A] 类型的列表中,因此是 r::_

    不使用递归的另一种解决方案是:

    def sequence[A](a: List[Option[A]])(implicit nullValue: A):Option[List[A]] = {
     Option(a map { x => x.getOrElse(nullValue)})
    } 
    

    【讨论】:

    • nullValue 取决于当元素为None 时用户希望放入列表中的内容。将不得不提及。例如:sequence(List(Some(1), Some(2),None))(-1)。如果用户希望省略None,那么另一个问题
    • 如果不是源集合的所有元素都是Some,则问题中的方法sequence 将返回None
    • 类似if (a.forall(_.isDefined)) Some(a.map{ _.get }) else None
    • @senia 好的。谢谢。那是我的错。我添加了一个默认值,以防它是无。
    • a map {_.getOrElse(default)} 更短。
    【解决方案3】:

    我找到了另一种实现 seq 函数的方法,它不使用隐式递归:

    def seq[A](a: List[Option[A]]):Option[List[A]] = a.foldLeft(Some(Nil):Option[List[A]])((collected,elem) => elem.flatMap(el=> collected.map(el::_)))
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-07-21
      • 2013-05-21
      • 1970-01-01
      • 2019-03-03
      • 2021-08-31
      • 2021-01-13
      • 2018-02-15
      • 2020-07-16
      相关资源
      最近更新 更多