2010-11-03 63 views
12

在迭代Scala循环时处理异常的最佳方式是什么?例如,如果我有一个可以抛出异常的convert()方法,我想捕获该异常,记录它并继续迭代。有没有一个“斯卡拉”的方式来做到这一点?Scala - 在地图中捕捉异常

理想情况下,我想是这样......

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 [ConvertedPoint])。任何帮助将不胜感激!

谢谢!

回答

5

也许你想要一个flatMap。下面是一个例子,应该看到它如何适应:-)

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

以上将使用副作用记录(印刷或将在可变的东西 - 如果这样做,确保评价是被迫! )。为了避免副作用和注意事项,映射函数可能只是Point -> Either[CovertedPoint,Exception],然后可以用Seq.partition或类似的结果分隔结果。

14

flatMap可能是你在寻找什么,但地图功能记录的副作用,如果马上点是一个视图可能不会出现这些副作用:

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(",")) 
+0

你偷了我的答案,但+1更好地解释它:-)欢迎来到SO。 – 2010-11-03 22:04:53

+1

同意 - 你的答案是100%正确(和一个灵感),但我认为增加更多的细节证明另一个答案,而不仅仅是一个评论。 :) – DaGGeRRz 2010-11-07 01:28:29

12

有趣了,我有很多的麻烦解释使用scala.util.control.Exception优于try/catch的优势,然后我开始看到可以从中得出完美例子的问题。

这里:

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)))