在scala

时间:2015-08-09 08:57:45

标签: scala functional-programming traversal

它将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%的元素。

4 个答案:

答案 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列出。如果要保留列表顺序,则必须

  1. seq(a, Some(Nil))替换为seq(a, Some(Nil)).map(_.reverse)
  2. 或将item :: list替换为list :+ item
  3. 但是,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))