使用Scalaz将选项列表转换为List选项

Raf*_*ira 28 scala option scalaz

我想把a List[Option[T]]变成a Option[List[T]].函数的签名类型是

def lo2ol[T](lo: List[Option[T]]): Option[List[T]]
Run Code Online (Sandbox Code Playgroud)

预期的行为是将仅包含Somes 的列表映射到Some包含元素内部元素的列表Some.另一方面,如果输入列表至少有一个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())
Run Code Online (Sandbox Code Playgroud)

没有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]]; 
}}}
Run Code Online (Sandbox Code Playgroud)

我记得在某个地方看过类似的例子,但是使用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
  }
}}
Run Code Online (Sandbox Code Playgroud)

Apo*_*isp 21

有一个函数可以将List[Option[A]]变成Option[List[A]]Scalaz中的函数.是的sequence.为了得到None任何元素,None以及Some[List[A]]所有元素都是Some,你可以这样做:

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

lo.sequence
Run Code Online (Sandbox Code Playgroud)

这种方法实际上变成F[G[A]G[F[A]]存在的实现Traverse[F],并且Applicative[G](Option并且List碰巧满足这两种并且由这些导入提供).

的语义Applicative[Option]是这样的,如果任何一种元素ListOptions为None,那么sequence将是None为好.如果要获取所有Some值的列表而不管其他值是否为None,则可以执行以下操作:

lo flatMap (_.toList)
Run Code Online (Sandbox Code Playgroud)

你可以概括为任何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))
Run Code Online (Sandbox Code Playgroud)


Rex*_*err 18

出于某种原因你不喜欢

if (lo.exists(_ isEmpty)) None else Some(lo.map(_.get))
Run Code Online (Sandbox Code Playgroud)

?这可能是没有Scalaz的Scala中最短的.

  • 不会`if(lo包含None)没有别的(lo.flatten)`甚至更短? (7认同)
  • 不错,但它在列表中翻了两遍. (3认同)
  • @Apocalisp:的确,但这几乎总是比丢弃新列表中的一半更便宜,然后丢弃它们并扔回"无". (3认同)