使用 Scalaz 将选项列表转换为列表选项
我想将 List[Option[T]]
转换为 Option[List[T]]
。函数的签名类型是
def lo2ol[T](lo: List[Option[T]]): Option[List[T]]
预期的行为是将仅包含 Some
的列表映射到包含元素 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())
没有 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
}
}}
I want to transform a List[Option[T]]
into a Option[List[T]]
. The signature type of the function is
def lo2ol[T](lo: List[Option[T]]): Option[List[T]]
The expected behavior is to map a list that contains only Some
s into a Some
containing 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:
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:
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?
A slightly more succinct version, using Scala2.8 PartialFunction.condOpt
, but still without 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
}
}}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
Scalaz 中有一个函数可以将
List[Option[A]]
转换为Option[List[A]]
。这是序列
。如果任何元素为None
,则获取None
;如果所有元素为,则获取
,你可以这样做:Some[List[A]]
有些鉴于存在一个实现,此方法实际上将
F[G[A]
转换为G[F[A]]
Traverse[F]
和Applicative[G]
(Option
和List
恰好满足两者并且是由这些进口提供)。Applicative[Option]
的语义是这样的:如果Option
的List
中的任何元素为None
,那么sequence
也将是None
。如果您想获取所有Some
值的列表,无论其他值是否为None
,您可以这样做:您可以将其推广到任何
Monad
也形成了Monoid
(List
恰好是其中之一):There's a function that turns a
List[Option[A]]
into anOption[List[A]]
in Scalaz. It'ssequence
. To getNone
in case any of the elements areNone
and aSome[List[A]]
in case all the elements areSome
, you can just do this:This method actually turns
F[G[A]
intoG[F[A]]
given that there exists an implementation ofTraverse[F]
, and ofApplicative[G]
(Option
andList
happen to satisfy both and are provided by those imports).The semantics of
Applicative[Option]
are such that if any of the elements of aList
ofOption
s areNone
, then thesequence
will beNone
as well. If you want to get a list of all theSome
values regardless of whether any other values areNone
, you can do this:You can generalize that for any
Monad
that also forms aMonoid
(List
happens to be one of these):由于某种原因你不喜欢
?这可能是没有 Scalaz 的 Scala 中最短的。
For some reason you dislike
? That's probably the shortest in Scala without Scalaz.
开始 Scala 2.13,并添加
Option::unless
构建器到标准库,Rex Kerr 的答案的一个变体是:或者,如果性能受到威胁(为了避免
flatten
从Option
到List
的隐式转换):Starting
Scala 2.13
, and the addition of theOption::unless
builder to the standard library, a variant to Rex Kerr's answer would be:or, if performance is at stake (in order to avoid
flatten
's implicit conversion fromOption
toList
):虽然 Scalaz 中的
Applicative[Option]
直接使用MA#sequence
的行为是错误的,但您也可以从Applicative
派生一个Applicative
>幺半群。使用MA#foldMapDefault
或MA#collapse
可以方便地实现这一点。在本例中,我们使用 Monoid[Option[List[Int]]。我们首先执行一个内部映射 (
MA#∘∘
),将各个Int
包装在一个元素的List
中。从
List
抽象到具有Traverse
、Pointed
和Monoid
实例的任何容器:遗憾的是,尝试编译此代码目前要么触发 #2741 要么让编译器进入无限循环。
更新
为了避免遍历列表两次,我应该使用
foldMapDefault
:这个答案基于原始请求,即应返回空列表或仅包含
None
的列表无
。顺便说一句,这最好通过Option[scalaz.NonEmptyList]
类型进行建模 -NonEmptyList
保证至少有一个元素。如果您只想要一个
List[Int]
,有很多更简单的方法,在其他答案中给出。没有提到的两种直接方式:While the
Applicative[Option]
in Scalaz has the wrong behaviour to directly useMA#sequence
, you can also derive anApplicative
from aMonoid
. This is made convenient withMA#foldMapDefault
orMA#collapse
.In this case, we use a
Monoid[Option[List[Int]]
. We first perform an inner map (MA#∘∘
) to wrap the individualInt
s inList
s of one element.Abstracting from
List
to any container with instances forTraverse
,Pointed
andMonoid
:Sadly, trying to compile this code currently either triggers #2741 or sends the compiler into an infinite loop.
UPDATE
To avoid traversing the list twice, I should have used
foldMapDefault
:This answer was based on the original request that an empty list, or a list containing only
None
s, should return aNone
. Incidentally, this would be best modeled by the typeOption[scalaz.NonEmptyList]
--NonEmptyList
guarantees at least one element.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:这对我有用。我希望这是一个正确的解决方案。
如果列表中的选项之一为 None,则返回 None,否则返回 List[A] 的选项
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]