我想转换一个 List[Option[T]] 进入一个 Option[List[T]].的签名类型的功能

def lo2ol[T](lo: List[Option[T]]): Option[List[T]]

预期的行为是地图上的一个列表,其中仅包含 Somes成 Some 含有清单的元素内部的元素 Some's。另一方面,如果输入的名单至少有一个 None, ,预期的行为是刚刚返回 None.例如:

scala> lo2ol(Some(1) :: Some(2) :: Nil)
res10: Option[List[Int]] = Some(List(1, 2))

scala> lo2ol(Some(1) :: None :: Some(2) :: Nil)
res11: Option[List[Int]] = None

scala> lo2ol(Nil : List[Option[Int]])
res12: Option[List[Int]] = Some(List())

一个例子实现,而不scalaz,将是:

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = {
  lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => (o, ol) match {
    case (Some(x), Some(xs)) => Some(x :: xs);
    case _ => None : Option[List[T]]; 
}}}

我记得我看到某个地方一个类似的例子,但是使用Scalaz简化的代码。如何将它看起来像什么?


一个稍微更加简明版本,使用Scala2.8 PartialFunction.condOpt, 但仍然没有Scalaz:

import PartialFunction._

def lo2ol[T](lo: List[Option[T]]): Option[List[T]] = {
  lo.foldRight[Option[List[T]]](Some(Nil)){(o, ol) => condOpt(o, ol) {
    case (Some(x), Some(xs)) => x :: xs
  }
}}
有帮助吗?

解决方案

有,轮流一个List[Option[A]]到Scalaz一个Option[List[A]]的功能。这是sequence。要获得None的情况下,任何元素都None和的情况下,所有的元素都Some[List[A]]一个Some,你可以这样做:

import scalaz.syntax.traverse._
import scalaz.std.list._     
import scalaz.std.option._

lo.sequence

这个方法实际上变成F[G[A]G[F[A]]鉴于存在Traverse[F]的实施方案,和Applicative[G]的(OptionList碰巧同时满足,并通过这些进口提供)。

Applicative[Option]的语义是使得如果任何Lists的Option的元件是None,那么sequenceNone为好。如果你想获得的所有Some值的列表,无论任何其他值是否None,你可以这样做:

lo flatMap (_.toList)

可以概括,对于还形成一个MonadMonoid恰好是其中之一)的任何List

import scalaz.syntax.monad._

def somes[F[_],A](x: F[Option[A]])
                 (implicit m: Monad[F], z: Monoid[F[A]]) =
  x flatMap (o => o.fold(_.pure[F])(z.zero))

其他提示

由于某种原因,你不喜欢

if (lo.exists(_ isEmpty)) None else Some(lo.map(_.get))

?这可能是在斯卡拉最短不Scalaz。

虽然Scalaz的Applicative[Option]有错误的行为直接使用MA#sequence,还可以得到从ApplicativeMonoid。这是由与方便或MA#foldMapDefault MA#collapse

在这种情况下,我们使用Monoid[Option[List[Int]]。我们首先进行的内映射(MA#∘∘)来包装个体Ints中的一个元素的Lists。

(List(some(1), none[Int], some(2)) ∘∘ {(i: Int) => List(i)}).collapse assert_≟ some(List(1, 2))
(List(none[Int]) ∘∘ {(i: Int) => List(i)}).collapse                   assert_≟ none[List[Int]]
(List[Option[Int]]() ∘∘ {(i: Int) => List(i)}).collapse               assert_≟ none[List[Int]]

List抽象到任何容器具有用于TraversePointedMonoid实例:

def co2oc[C[_], A](cs: C[Option[A]])
                  (implicit ct: Traverse[C], cp: Pointed[C], cam: Monoid[C[A]]): Option[C[A]] =
  (cs ∘∘ {(_: A).pure[C]}).collapse


co2oc(List(some(1), none[Int], some(2)))   assert_≟ some(List(1, 2))
co2oc(Stream(some(1), none[Int], some(2))) assert_≟ some(Stream(1, 2))
co2oc(List(none[Int]))                     assert_≟ none[List[Int]]
co2oc(List[Option[Int]]())                 assert_≟ none[List[Int]]

不幸的是,试图编译该代码目前要么触发器#2741 或发送编译器进入无限循环。

<强>更新 为了避免遍历列表两次,我应该用foldMapDefault

(List(some(1), none[Int], some(2)) foldMapDefault (_ ∘ ((_: Int).pure[List])))

此答案是基于原始请求,一个空的列表,或仅含有Nones列表,应返回None。顺便说一下,这将被最好由类型Option[scalaz.NonEmptyList]建模 - NonEmptyList保证的至少一种元素

如果你只是想在一个List[Int],有很多更容易的方式,在其他的答案中给出。还没有被提到了两个直接的方式:

list collect { case Some(x) => x }
list flatten

这为我工作。我希望这是一个正确的解决方案。

它返回无如果在列表中的选项之一是无,否则返回列表[A]

的选项
def sequence[A](a: List[Option[A]]): Option[List[A]] = {

  a.foldLeft(Option(List[A]())) {
    (prev, cur) => {

      for {
        p <- prev if prev != None
        x <- cur
      } yield x :: p

    }
  }

}

开始 Scala 2.13, 和另外的 Option::unless 建设者的 标准图书馆, 一个变种, 雷克斯*科尔的答案 将是:

Option.unless(list contains None)(list.flatten)
// val list = List(Some(1), Some(2))          =>    Some(List(1, 2))
// val list = List(Some(1), None, Some(2))    =>    None

或者,如果性能危在旦夕(以避免 flatten's隐性转换 OptionList):

Option.unless(list contains None)(list.map(_.get))
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top