30

我想将 aList[Option[T]]转换为 a Option[List[T]]。函数的签名类型是

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

预期的行为是将仅包含Somes 的列表映射到Some包含 elements 中的元素列表的列表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())

没有 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
  }
}}
4

5 回答 5

23

在 Scalaz 中有一个函数可以将 aList[Option[A]]转换为a。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]是这样的,如果s的 aList的任何元素是,那么也将是。如果您想获取所有值的列表,而不管其他值是否为,您可以执行以下操作:OptionNonesequenceNoneSomeNone

lo flatMap (_.toList)

您可以将其概括为任何Monad也形成MonoidList恰好是其中之一)的:

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))
于 2010-04-03T00:43:34.543 回答
20

出于某种原因你不喜欢

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

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

于 2010-04-03T00:11:39.973 回答
3

开始Scala 2.13,并将Option::unless构建器添加到标准库中, Rex Kerr 的答案的变体将是:

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' 从Optionto的隐式转换List):

Option.unless(list contains None)(list.map(_.get))
于 2019-03-09T12:54:46.710 回答
2

虽然Applicative[Option]Scalaz 中的 直接使用有错误的行为MA#sequence,但您也可以Applicative从 a派生 an Monoid。这很方便使用MA#foldMapDefaultor MA#collapse

在这种情况下,我们使用Monoid[Option[List[Int]]. 我们首先执行一个内部映射 ( ) 以将单个sMA#∘∘包装在一个元素的 s 中。IntList

(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 的列表应返回 a None。顺便说一句,这最好按类型建模Option[scalaz.NonEmptyList]——NonEmptyList保证至少一个元素。

如果您只想要 a List[Int],则在其他答案中给出了许多更简单的方法。没有提到的两种直接方式:

list collect { case Some(x) => x }
list flatten
于 2010-04-03T06:38:57.237 回答
1

这对我有用。我希望这是一个正确的解决方案。

如果 List 中的选项之一为 None,则返回 None,否则返回 List[A] 的 Option

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

    }
  }

}
于 2018-03-05T22:56:22.413 回答