16

try のコレクションを平坦化して、try 値の成功または単に失敗のいずれかを与える簡単な方法はありますか? 例えば:

def map(l:List[Int]) = l map {
  case 4 => Failure(new Exception("failed"))
  case i => Success(i)
}

val l1 = List(1,2,3,4,5,6)
val result1 = something(map(l1))

result1: Failure(Exception("failed"))

val l2 = List(1,2,3,5,6)
val result2 = something(map(l2)) 

result2: Try(List(1,2,3,5,6))

また、コレクション内の複数の失敗をどのように処理できますか?

4

7 に答える 7

29

これは、フェイルファースト操作の最小値にかなり近いです。

def something[A](xs: Seq[Try[A]]) =
  Try(xs.map(_.get))

(わざわざメソッドを作成するべきではないところまで; を使用するだけですTry)。すべての失敗が必要な場合は、メソッドが合理的です。私は使用しますEither

def something[A](xs: Seq[Try[A]]) =
  Try(Right(xs.map(_.get))).
  getOrElse(Left(xs.collect{ case Failure(t) => t }))
于 2013-03-19T13:00:07.800 に答える
9

少し冗長で、より型安全です。

def sequence[T](xs : Seq[Try[T]]) : Try[Seq[T]] = (Try(Seq[T]()) /: xs) {
    (a, b) => a flatMap (c => b map (d => c :+ d))
}

結果:

sequence(l1)

res8: scala.util.Try[Seq[Int]] = 失敗 (java.lang.Exception: 失敗)

sequence(l2)

res9: scala.util.Try[Seq[Int]] = Success(List(1, 2, 3, 5, 6))

于 2013-03-19T10:55:55.780 に答える
8

あなたが望んでいたほど単純ではないかもしれませんが、これはうまくいきます:

def flatten[T](xs: Seq[Try[T]]): Try[Seq[T]] = {
  val (ss: Seq[Success[T]]@unchecked, fs: Seq[Failure[T]]@unchecked) =
    xs.partition(_.isSuccess)

  if (fs.isEmpty) Success(ss map (_.get))
  else Failure[Seq[T]](fs(0).exception) // Only keep the first failure
}

val xs = List(1,2,3,4,5,6)
val ys = List(1,2,3,5,6)

println(flatten(map(xs))) // Failure(java.lang.Exception: failed)
println(flatten(map(ys))) // Success(List(1, 2, 3, 5, 6))

の使用はpartition、注釈が示すように、タイプ セーフではないことに注意してください@unchecked。その点では、foldLeft2 つのシーケンスを累積しSeq[Success[T]]Seq[Failure[T]]より良いでしょう。

すべての失敗を保持したい場合は、これを使用できます。

def flatten2[T](xs: Seq[Try[T]]): Either[Seq[T], Seq[Throwable]] = {
  val (ss: Seq[Success[T]]@unchecked, fs: Seq[Failure[T]]@unchecked) =
    xs.partition(_.isSuccess)

  if (fs.isEmpty) Left(ss map (_.get))
  else Right(fs map (_.exception))
}

val zs = List(1,4,2,3,4,5,6)

println(flatten2(map(xs))) // Right(List(java.lang.Exception: failed))
println(flatten2(map(ys))) // Left(List(1, 2, 3, 5, 6))
println(flatten2(map(zs))) // Right(List(java.lang.Exception: failed, 
                           //            java.lang.Exception: failed))
于 2013-03-19T10:29:48.067 に答える
6

Impredicativeの回答とコメントへの追加として、依存関係にscalaz-sevenscalaz-contrib /scala210の両方がある場合:

> scala210/console
[warn] Credentials file /home/folone/.ivy2/.credentials does not exist
[info] Starting scala interpreter...
[info] 
Welcome to Scala version 2.10.0 (OpenJDK 64-Bit Server VM, Java 1.7.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.util._
import scala.util._

scala> def map(l:List[Int]): List[Try[Int]] = l map {
     |   case 4 => Failure(new Exception("failed"))
     |   case i => Success(i)
     | }
map: (l: List[Int])List[scala.util.Try[Int]]

scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._

scala> import scalaz.contrib.std.utilTry._
import scalaz.contrib.std.utilTry._

scala> val l1 = List(1,2,3,4,5,6)
l1: List[Int] = List(1, 2, 3, 4, 5, 6)

scala> map(l1).sequence
res2: scala.util.Try[List[Int]] = Failure(java.lang.Exception: failed)

scala> val l2 = List(1,2,3,5,6)
l2: List[Int] = List(1, 2, 3, 5, 6)

scala> map(l2).sequence
res3: scala.util.Try[List[Int]] = Success(List(1, 2, 3, 5, 6))

メソッドを取得するには、(インスタンスに非表示になっている)のApplicative インスタンスを取得するためにscalazが必要です。のインスタンスにはscalaz-contribが必要です。これは、の型アノテーションに必要です。 scalaz 2.10にのみ登場し、scalazは以前のバージョンへのクロスコンパイルを目的としているため、scalazの外部に存在します。ListMonadPlussequenceTraverse TrysequenceTry

于 2013-03-19T12:59:55.633 に答える
3

liftweb Box モナドを見てください。コンストラクター関数の助けを借りて、tryo探している抽象化を正確に提供します。

を使用tryoすると、関数を に持ち上げることができますBox。ボックスには、関数の結果が含まれているか、エラーが含まれています。その後、ボックスにエラーや関数からの結果が含まれているかどうかを気にすることなく、通常のモナド ヘルパー関数 (flatMap、filter など) を使用してボックスにアクセスできます。

例:

import net.liftweb.util.Helpers.tryo

List("1", "2", "not_a_number") map (x => tryo(x.toInt)) map (_ map (_ + 1 ))

する結果

List[net.liftweb.common.Box[Int]] = 
  List(
    Full(2), 
    Full(3), 
    Failure(For input string: "not_a_number",Full(java.lang.NumberFormatException: For input string: "not_a_number"),Empty)
  )

誤った値をスキップできますflatMap

List("1", "2", "not_a_number") map (x => tryo(x.toInt)) flatMap (_ map (_ + 1 ))

結果

List[Int] = List(2, 3)

他にも複数のヘルパー メソッドがあります。たとえば、ボックスの結合 (エラー メッセージの連鎖中) などです。ここで概要を確認できます: Box Cheat Sheet for Lift

Lift フレームワーク全体を使用する必要はなく、単独で使用できます。上記の例では、次の sbt スクリプトを使用しました。

scalaVersion := "2.9.1"

libraryDependencies += "net.liftweb" %% "lift-common" % "2.5-RC2"

libraryDependencies += "net.liftweb" %% "lift-util" % "2.5-RC2"
于 2013-03-19T12:23:04.787 に答える