18

Scalaでループを繰り返している間に例外を処理する最良の方法は何ですか?

たとえばconvert()、例外をスローできるメソッドがあった場合、その例外をキャッチしてログに記録し、反復を続けたいと思います。これを行う「スカラ」の方法はありますか?

理想的には、次のようなものが欲しいです...

val points: Seq[Point] = ...
val convertedPoints: Seq[ConvertedPoint] = points.map(
   p => {
     try { p.convert() } 
     catch { case ex: Exception => logger.error("Could not convert", ex) }
})

あるリストから別のリストへの直接マッピングではないため、上記のコードを実行することはできません ( とはSeq[Any]対照的に戻りますSeq[ConvertedPoint])。

4

4 に答える 4

17

/を使用scala.util.control.Exceptionすることの利点を説明するのに苦労したことは興味深いことです。trycatch

ここ:

import scala.util.control.Exception._
List(1, 23, 5, 2, 0, 3, 2) flatMap (x => catching(classOf[Exception]) opt (10 / x))

独自のコードは次のようになります。

val points: Seq[Point] = ...
val convertedPoints: Seq[ConvertedPoint] = points.flatMap(
  p => handling(classOf[Exception]) by { ex =>
    logger.error("Could not convert", ex); None
  } apply Some(p.convert)
)

または、リファクタリングする場合:

val exceptionLogger = handling(classOf[Exception]) by { ex =>
    logger.error("Could not convert", ex); None
}
val convertedPoints: Seq[ConvertedPoint] = points.flatMap(p => exceptionLogger(Some(p.convert)))
于 2010-11-03T23:44:48.417 に答える
16

flatMap はおそらくあなたが探しているものですが、 map 関数にはロギングの副作用があり、ポイントがビューの場合、これらの副作用はすぐには発生しない可能性があります。

val convertedPoints = points.view.flatMap { p =>
  try { 
    Some(p.convert) 
  } catch {
    case e : Exception =>
    // Log error
    None
  }
}
println("Conversion complete")
println(convertedPoints.size + " were converted correctly")

これは次のように表示されます。

Conversion complete
[Error messages]
x were converted correctly

あなたの場合、ビューをドロップすると、おそらく問題ありません。:)

変換を純粋な関数 (副作用なし) にするには、おそらく [Either] を使用します。ここで努力する価値はないと思いますが(実際にエラーを処理したい場合を除きます)、これを使用したかなり完全な例を次に示します。

case class Point(x: Double, y: Double) {
  def convert = {
    if (x == 1.0) throw new ConversionException(this, "x is 1.0. BAD!")
    else ConvertedPoint(x, y)
  }
}
case class ConvertedPoint(x: Double, y: Double)
class ConversionException(p: Point, msg: String) extends Exception(msg: String)


val points = List(Point(0,0), Point(1, 0), Point(2,0))

val results = points.map { p =>
  try {
    Left(p.convert)
  } catch {
    case e : ConversionException => Right(e)
  }
}

val (convertedPoints, errors) = results.partition { _.isLeft }

println("Converted points: " + convertedPoints.map(_.left.get).mkString(","))
println("Failed points: " + errors.map( _.right.get).mkString(","))
于 2010-11-03T19:42:29.537 に答える
6

おそらく、flatMap が必要です。ここに例を示します。どのように適合するかを確認してください:-)

List(1,2,3,4).flatMap(x => if (x > 2) Some(x) else None)

上記は、副作用のロギングを使用します (変更可能なものを印刷または配置します。これが行われる場合は、評価が強制されることを確認してください!)。副作用と警告を回避するために、マッピング関数を of にするだけでPoint -> Either[CovertedPoint,Exception]、結果を分離Seq.partitionまたは類似させることができます。

于 2010-11-03T17:04:26.053 に答える