使用Map而不是FlatMap将List [Option [A]]转换为Option [List [A]]

时间:2013-06-24 05:03:53

标签: list scala map higher-order-functions

在书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::_))
}

我对h flatMap (r => sequence(t) map (h::_))部分感到有些困惑。如果我将其分解,则如下:

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]]的转换吗?此外,提供的解决方案似乎不是尾递归。它可以被尾递归吗?

3 个答案:

答案 0 :(得分:3)

有一个拼写错误:

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

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

sequence(t)返回Option[List[A]]map (r::_)上的Option[List[A]]不会更改类型。它只需List[A] Option 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 }

实际上它可以完全不是递归的:

{{1}}

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:1)

我找到了另一种实现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::_)))