30

List[Option[T]]をに変換したいOption[List[T]]。関数のシグネチャタイプは次のとおりです。

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

Some予想される動作は、 sのみSomeを含むリストを要素の内部の要素のリストを含むリストにマップすることSomeです。一方、入力リストに少なくとも1つある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を使用しているが、 Scalazを使用していない、もう少し簡潔なバージョンPartialFunction.condOpt

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]]をに変換する関数があります。Option[List[A]]ですsequenceNoneいずれかの要素が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]OptionList

のセマンティクスは、 sのaのApplicative[Option]要素のいずれかがである場合、も同様になるようなものです。他の値がであるかどうかに関係なく、すべての値のリストを取得する場合は、次のように実行できます。ListOptionNonesequenceNoneSomeNone

lo flatMap (_.toList)

これを一般化して、 (たまたまこれらの1つである)を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に追加すると、RexKerrの答えの変形は次のようになります。Option::unless

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の暗黙の変換を回避するため):OptionList

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

Applicative[Option]in Scalazは、直接使用するには間違った動作をしますが、からMA#sequence派生することもできます。これはまたはで便利になります。ApplicativeMonoidMA#foldMapDefaultMA#collapse

この場合、を使用しMonoid[Option[List[Int]]ます。最初に内部マップ()を実行して、個々のsを1つの要素のsでMA#∘∘ラップします。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がトリガーされるか、コンパイラーが無限ループに陥ります。

更新 リストを2回トラバースすることを避けるために、私は以下を使用する必要がありましたfoldMapDefault

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

この回答は、空のリスト、またはNonesのみを含むリストが。を返す必要があるという元の要求に基づいていましたNone。ちなみに、これはタイプによって最もよくモデル化されますOption[scalaz.NonEmptyList]-NonEmptyList少なくとも1つの要素を保証します。

aだけが必要な場合はList[Int]、他の回答に示されているように、より簡単な方法がたくさんあります。言及されていない2つの直接的な方法:

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

これは私のために働いた。これが正しい解決策であることを願っています。

リスト内のオプションの1つがNoneの場合はNoneを返し、それ以外の場合はOption ofList[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

    }
  }

}
于 2018-03-05T22:56:22.413 に答える