它将Option
列表合并为一个Option
,其中包含原始列表中所有Some
值的列表。如果原始列表包含None
一次,则函数的结果应为None
,否则结果应为Some
,其中包含所有值的列表。签名是
def sequence[A](a: List[Option[A]]): Option[List[A]]
我提出了以下解决方案
def sequence[A](l: List[Option[A]]): Option[List[A]] = {
if (l.exists(_.isEmpty)) None
else Some(l.map(_.get))
}
它似乎并不完美,因为它迭代列表并将f
函数应用两次以平均50%的元素。
答案 0 :(得分:5)
这是一个快速而肮脏的解决方案,肯定会冒犯每个人的良好功能感受:)
import scala.util.Try
def sequence[A](xs: List[Option[A]]): Option[List[A]] =
Try(xs.map(_.get)).toOption
答案 1 :(得分:2)
这是一种可能性:
import scala.annotation.tailrec
def sequence[A](a: List[Option[A]]): Option[List[A]] = {
@tailrec
def seq(remaining: List[Option[A]], result: Option[List[A]]): Option[List[A]] = {
if (remaining.isEmpty) {
result
} else {
(remaining.head, result) match {
case (Some(item), Some(list)) => seq(remaining.tail, Some(item :: list))
case _ => None
}
}
}
seq(a, Some(Nil))
}
一旦遇到第一个None
元素,它就会停止评估列表。并且应该返回与您的实现相同的结果。但是,注意非常重要的是,此实现将为空输入列表返回Some(Nil)。
根据您对表现力和其他可读性标准的偏好,可以缩短实施时间:
def sequence[A](a: List[Option[A]]): Option[List[A]] = {
@tailrec
def seq(remaining: List[Option[A]], result: Option[List[A]]): Option[List[A]] = {
(remaining, result) match {
case (Nil, _) => result
case (Some(item) :: tail, Some(list)) => seq(tail, Some(item :: list))
case _ => None
}
}
seq(a, Some(Nil))
}
结果将Some
按相反顺序或None
列出。如果要保留列表顺序,则必须
seq(a, Some(Nil))
替换为seq(a, Some(Nil)).map(_.reverse)
item :: list
替换为list :+ item
。 但是,list :+ item
不是将项目附加到List
的最佳方式。如果您想保留订单并使用' @ tailrec'方法我建议使用Vector
作为结果类型而不是List
。
答案 2 :(得分:2)
快速失败的非功能性风格:
def sequence[A](xs: List[Option[A]]): Option[List[A]] =
Some(xs map (_.getOrElse(return None))) //this return (inside lambda) is implemented with exception in scala
递归一:
def sequence[A](xs: List[Option[A]]): Option[List[A]] = xs match {
case Nil => None //or `Some(Nil)`
case None :: tail => None
case Some(head) :: Nil => Some(head :: Nil)
case Some(head) :: tail => sequence(tail).map(head :: _)
}
Vector
- 基于N
(非1.5 * N)步骤,但没有快速失败:
def sequence[A](xs: Vector[Option[A]]): Option[Vector[A]] =
Some(xs.flatten) filter (_.size == xs.size) //getting size is fast for vector
基于快速失败的 Vector
+ view
:
//`view` doesn't create intermidiate collection and applies everything in one iteration
def sequence[A](xs: Vector[Option[A]]): Option[Seq[A]] =
Some(xs.view.takeWhile(_.nonEmpty).map(_.get).force) filter (_.size == xs.size)
无论如何,只有性能测试会在这里告诉你真实的效果。所有这些算法(包括你的算法)都是线性O(N),无论如何它都是最好的复杂性。因此,进一步的优化可能不值得。
答案 3 :(得分:1)
def sequence[A](xs: List[Option[A]]): Option[List[A]] = xs match {
case x :: xs => // Get one element at a time (Opton[A])
for {
a <- x // unwrap the Option[A] to A
as <- sequence(xs) // Unwrap the recursive result of Option[List[A]] into List[A]
} yield a :: as // Merge the elements and warp in Option
case _ => Some(Nil) // sequence of an empty list is Some(List())
}
println(sequence(List[Some[Int]]())) // Some(List())
println(sequence(List(Some(1), None, Some(3)))) // None
println(sequence(List(Some(1), Some(2), Some(3)))) // Some(List(1, 2, 3))