代码之家  ›  专栏  ›  技术社区  ›  Rafael de F. Ferreira

使用scalaz将选项列表转换为列表选项

  •  28
  • Rafael de F. Ferreira  · 技术社区  · 14 年前

    我想改变一个 List[Option[T]] 变成一个 Option[List[T]] . 函数的签名类型是

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

    预期的行为是映射仅包含 Some 进入一个 一些 包含元素内元素的列表 一些 另一方面,如果输入列表至少有一个 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
      }
    }}
    
    5 回复  |  直到 14 年前
        1
  •  20
  •   Apocalisp    8 年前

    有一个函数 List[Option[A]] Option[List[A]] 在Scalaz。它是 sequence . 得到 None 如果任何元素是 没有 和A 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是 没有 然后 序列 没有 也。如果你想得到所有 一些 不管是否有其他值 没有 ,您可以执行以下操作:

    lo flatMap (_.toList)
    

    你可以把它推广到任何 Monad 这也形成了 Monoid ( 恰好是其中之一):

    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))
    
        2
  •  18
  •   Rex Kerr    14 年前

    因为某些你不喜欢的原因

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

    ?这可能是scala中没有scalaz的最短的。

        3
  •  2
  •   retronym    14 年前

    Applicative[Option] 在scalaz中有错误的行为可以直接使用 MA#sequence ,您还可以导出 Applicative 从A Monoid . 这很方便 MA#foldMapDefault MA#collapse .

    在这种情况下,我们使用 Monoid[Option[List[Int]] . 我们首先执行一个内部映射( MA#∘∘ )包裹个人 Int S在 List 一个元素的。

    (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]]
    

    摘自 到任何具有实例的容器 Traverse , Pointed 幺半群 :

    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,应该返回A 没有 . 顺便说一句,这最好由 Option[scalaz.NonEmptyList] —— NonEmptyList 保证至少一个元素。

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

    list collect { case Some(x) => x }
    list flatten
    
        4
  •  1
  •   Dmitri    6 年前

    这对我有效。我希望这是一个正确的解决办法。

    如果列表中的某个选项为“无”,则返回“无”,否则返回列表[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
    
        }
      }
    
    }
    
        5
  •  0
  •   Xavier Guihot    5 年前

    启动 Scala 2.13 ,以及 Option::unless 建造者 标准库 ,是 Rex Kerr's answer 将是:

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

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