题
我想转换一个 List[Option[T]]
进入一个 Option[List[T]]
.的签名类型的功能
def lo2ol[T](lo: List[Option[T]]): Option[List[T]]
预期的行为是地图上的一个列表,其中仅包含 Some
s成 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]
的(Option
和List
碰巧同时满足,并通过这些进口提供)。
Applicative[Option]
的语义是使得如果任何List
s的Option
的元件是None
,那么sequence
将None
为好。如果你想获得的所有Some
值的列表,无论任何其他值是否None
,你可以这样做:
lo flatMap (_.toList)
可以概括,对于还形成一个Monad
(Monoid
恰好是其中之一)的任何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
,还可以得到从Applicative
的Monoid
。这是由与方便或MA#foldMapDefault
MA#collapse
。
在这种情况下,我们使用Monoid[Option[List[Int]]
。我们首先进行的内映射(MA#∘∘
)来包装个体Int
s中的一个元素的List
s。
(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
抽象到任何容器具有用于Traverse
,Pointed
和Monoid
实例:
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])))
此答案是基于原始请求,一个空的列表,或仅含有None
s列表,应返回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隐性转换 Option
要 List
):
Option.unless(list contains None)(list.map(_.get))