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