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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2569014/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:02:34  来源:igfitidea点击:

Convert a List of Options to an Option of List using Scalaz

scalaoptionscalaz

提问by Rafael de F. Ferreira

I want to transform a List[Option[T]]into a Option[List[T]]. The signature type of the function is

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

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

The expected behavior is to map a list that contains only Somes into a Somecontaining a list of the elements inside the elements Some's. On the other hand, if the input list has at least one None, the expected behavior is to just return None. For example:

预期的行为是将仅包含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())

An example implementation, without scalaz, would be:

一个没有 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]]; 
}}}

I remember seeing somewhere a similar example, but using Scalaz to simplify the code. How would it look like?

我记得在某处看到过一个类似的例子,但使用 Scalaz 来简化代码。它会是什么样子?



A slightly more succinct version, using Scala2.8 PartialFunction.condOpt, but still without 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
  }
}}

采纳答案by Apocalisp

There's a function that turns a List[Option[A]]into an Option[List[A]]in Scalaz. It's sequence. To get Nonein case any of the elements are Noneand a Some[List[A]]in case all the elements are Some, you can just do this:

有一个函数可以将Scalaz 中的aList[Option[A]]变成 an Option[List[A]]。它是sequence。要获取None任何元素是NoneSome[List[A]]情况以及所有元素都是的情况Some,您可以这样做:

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

lo.sequence

This method actually turns F[G[A]into G[F[A]]given that there exists an implementation of Traverse[F], and of Applicative[G](Optionand Listhappen to satisfy both and are provided by those imports).

这个方法实际上变成F[G[A]G[F[A]]假设存在Traverse[F], 和 of Applicative[G](Option并且List恰好满足这两个并且由这些导入提供) 的实现。

The semantics of Applicative[Option]are such that if any of the elements of a Listof Options are None, then the sequencewill be Noneas well. If you want to get a list of all the Somevalues regardless of whether any other values are None, you can do this:

的语义Applicative[Option]是这样的,如果s的 aList的任何元素OptionNone,那么 the sequencewillNone也是。如果您想获取所有Some值的列表,而不管其他值是否为None,您可以这样做:

lo flatMap (_.toList)

You can generalize that for any Monadthat also forms a Monoid(Listhappens to be one of these):

您可以将其概括为任何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))

回答by Rex Kerr

For some reason you dislike

出于某种原因你不喜欢

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

? That's probably the shortest in Scala without Scalaz.

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

回答by retronym

While the Applicative[Option]in Scalaz has the wrong behaviour to directly use MA#sequence, you can also derive an Applicativefrom a Monoid. This is made convenient with MA#foldMapDefaultor MA#collapse.

虽然Applicative[Option]Scalaz 中的 直接使用有错误的行为MA#sequence,但您也可以ApplicativeMonoid. 使用MA#foldMapDefault或可以方便地做到这一点MA#collapse

In this case, we use a Monoid[Option[List[Int]]. We first perform an inner map (MA#°°) to wrap the individual Ints in Lists of one element.

在这种情况下,我们使用Monoid[Option[List[Int]]. 我们首先执行内部映射 ( MA#°°) 将单个Ints包装在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]]

Abstracting from Listto any container with instances for Traverse, Pointedand Monoid:

从,和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]]

Sadly, trying to compile this code currently either triggers #2741or sends the compiler into an infinite loop.

可悲的是,当前尝试编译此代码会触发#2741或将编译器发送到无限循环中。

UPDATETo avoid traversing the list twice, I should have used foldMapDefault:

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

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

This answer was based on the original request that an empty list, or a list containing only Nones, should return a None. Incidentally, this would be best modeled by the type Option[scalaz.NonEmptyList]-- NonEmptyListguarantees at least one element.

此答案基于原始请求,即空列表或仅包含Nones的列表应返回 a None。顺便说一下,这最好通过类型来建模Option[scalaz.NonEmptyList]——NonEmptyList保证至少有一个元素。

If you just want the a List[Int], there are many easier ways, given in other answers. Two direct ways that haven't been mentioned:

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

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

回答by Dmitri

This worked for me. I hope this is a correct solution.

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

It returns None if one of the Options in the List is None, otherwise it returns Option of List[A]

如果列表中的选项之一为无则返回无,否则返回列表[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

    }
  }

}

回答by Xavier Guihot

Starting Scala 2.13, and the addition of the Option::unlessbuilder to the standard library, a variant to Rex Kerr's answerwould be:

开始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

or, if performance is at stake (in order to avoid flatten's implicit conversion from Optionto List):

或者,如果性能受到威胁(为了避免flattenOption到的隐式转换List):

Option.unless(list contains None)(list.map(_.get))