2

processOne次のように定義された呼び出されたモナド関数があるとします。

def processOne(input: Input): Either[ErrorType, Output] = ...

" "のリストが与えられた場合、対応する " " のリストを:でラップしInputsて返したいと思います。OutputsEither

def processMany(inputs: Seq[Input]): Either[ErrorType, Seq[Output]] = ...

processManyは、それが持っている各入力に対して呼び出しますが、 aを返すprocessOne最初の時間 (もしあれば) を終了し、それを返し、それ以外の場合は出力のリストと共に a を返します。processOneLeftLeftRight

私の質問: 実装する最良の方法は何processManyですか? 式を使用してこの動作を実現することは可能forですか、それともリストを自分で再帰的に繰り返す必要がありますか?

4

3 に答える 3

5

Scalaz 7では:

def processMany(inputs: Seq[Input]): Either[ErrorType, Seq[Output]] =
  inputs.toStream traverseU processOne

inputsへの変換Stream[Input]は、 の非厳密なtraverse実装をStream利用します。つまり、必要な短絡動作が得られます。

ところで、あなたはこれを「モナド」とタグ付けしましたが、トラバーサルにはアプリカティブ ファンクターしか必要ありません (たまたま、モナド for で定義されている可能性がありますEither)。詳細については、論文The Essence of the Iterator Pattern を参照するか、Scala ベースの解釈については、このテーマに関する Eric Torreborre のブログ投稿を参照してください。

于 2013-02-27T17:44:31.587 に答える
2

必要以上に評価しない標準の Scala で最も簡単なのは、おそらく

def processMany(inputs: Seq[Input]): Either[ErrorType, Seq[Output]] = {
  Right(inputs.map{ x =>
    processOne(x) match {
      case Right(r) => r
      case Left(l) => return Left(l)
    }
  })
}

折り畳みはよりコンパクトになりますが、左にヒットしても短絡しません (入力全体を反復処理している間、折り畳みを続けます)。

于 2013-02-27T17:30:11.470 に答える
0

今のところ、ライブラリ (Scalaz) に依存関係を追加するのは気が進まないので、再帰を使用してこれを解決することにしました。

(私のアプリケーションの型と名前は、より一般的に見えるようにここで変更されています)

def processMany(inputs: Seq[Input]): Either[ErrorType, Seq[Output]] = {
  import scala.annotation.tailrec

  @tailrec
  def traverse(acc: Vector[Output], inputs: List[Input]): Either[ErrorType, Seq[Output]]  = {
    inputs match {
      case Nil =>   Right(acc)
      case input :: more =>
          processOne(input) match {
            case Right(output) =>  traverse(acc :+ output, more)
            case Left(e) => Left(e)
          }
    }
  }

  traverse(Vector[Output](), inputs.toList)
}
于 2013-02-27T19:20:17.180 に答える