2

私はいくつかのコードを持っているとしましょう:

def foo(s:String):Either[Bar, Baz] = // some code here ... 

私はそれを次のように使いたい:

val a = Array("a", "b", "c")
a.map(foo) match {
   case something => // should match when all elements in array are of type Right
   case _ =>
}

誰でも「何か」のコードを提案できますか

Right[Bar,Baz]編集:一致後に抽出するよりも、配列を直接一致させて使用することをお勧めします。

4

3 に答える 3

2

メソッドを使用forallして、配列内のすべての要素が であることを確認しますisRight

a.map(foo) match {
  case eithers if eithers.forall(_.isRight) =>
  case _ =>
}

あなたのコメントに関して、一致と右への変換をすべて 1 つのパスで行う場合は、カスタム エクストラクタを試してください。

object RightArrayExtractor {
  def unapply(eithers: Array[Either[Bar, Baz]]) =
    eithers.foldLeft(Option(Vector[Right[Bar, Baz]]())) {
      case (Some(z), x @ Right(_)) => Some(z :+ x)
      case (None, _) => None
      case (_, Left(x)) => None
    }
}

a.map(foo) match {
  case RightArrayExtractor(eithers) => // eithers is a Vector[Right[Bar,Baz]]
  case _ =>
}
于 2012-09-22T05:10:25.633 に答える
1

他の回答は非常に優れており、この例は他の回答ほど実用的ではありません。基礎となる理論を少し追加したいと思います。

あなたが説明していることは、関数型プログラミングではトラバーサルと呼ばれることがよくあります。のようなコレクションSeq[X]とモナド (または適用可能な) 計算がありX => M[Y]ます。標準でmapは が得られますSeq[M[Y]]が、トラバーサルでは が得られますM[Seq[Y]]

この場合、モナド計算は を生成するものでEither[Error,Right]あり、この場合M[_]Either[Error,_]です。したがって、そのような関数を使用してコレクションをマップすると、Seq[Either[Error,Right]]. しかし、あなたが望むEither[Error,Seq[Right]]のは、まさにトラバーサルが行うことです。関数がシーケンスのいずれかの要素で失敗した場合 ( を返しますLeft(something))、最終結果は this になりLeft(something)ます。関数がすべての要素で成功した場合 (すべての要素を返すRight(...))、最終結果はRight(sequenceOfResults)です。

Scala にはそのための組み込み関数がありませんが、Scalazにはありtraverseます。完全な例:

import scalaz._;
import Scalaz._;
import Applicative._;

object RightMatch extends App {
  // our example function
  def foo(s: String): Either[String,Int] =
    if (s.startsWith("a")) Right(s.length)
    else Left("wrong: " + s);

  // We make an utility function for traversing Sequences wit Eithers:
  def traverseRight[X,L,R](es: Seq[X], f: X => Either[L,R]): Either[L,Seq[R]] = {
    // we need to convert Either to Either.RightProjection
    type RightF[Y] = Either.RightProjection[L,Y];
    es.traverse[RightF,R](x => f(x).right).e; // and back to Either
  }

  // Or, if we just want to convert an existing sequence of eithers:
  def traverseRight[L,R](es: Seq[Either[L,R]]): Either[L,Seq[R]] =
    traverseRight(es, identity[Either[L,R]]);

  {
    val a = Seq("a", "ab", "ac");
    traverseRight(a, foo) match {
      case Right(arr) => println(arr); // we get the array of Ints here
      case Left(err)  => println(err); // we get String here (the first error)
    }
  }
}

(ScalazArrayには traversable の実装がないことに注意してください (理由はわかりません)、Seq代わりに使用しました。)

前述のように、トラバーサルはEithers だけでなく、すべてのモナド計算に適用されます。Stateしたがって、ステートフルな計算 (scalaz によってモデル化された) の順序付け、非決定論的計算 (Listモナド)の順序付けなど、幅広い問題に同じアプローチを使用できます。

于 2012-09-24T08:10:18.460 に答える
1

の便利さを使用してcollect、配列を型に変更できますRight(しゃれは意図されていません)。また、すべての要素が の場合、収集された配列は同じサイズになりますRight

a.map(foo).collect{case r@Right(_) => r} match {
  case a1 if a1.size == a.size =>
    // do something with a1 of type Array[Right[Bar,Baz]]
  case _ => 
}
于 2012-09-22T06:38:58.780 に答える