scala 在地图中捕获异常
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4089537/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Catching an exception within a map
提问by shj
What is the best way of handling exceptions while iterating over a loop in Scala?
在 Scala 中迭代循环时处理异常的最佳方法是什么?
For instance, if I had a convert()method that could throw an exception, I'd like to catch that exception, log it, and keep iterating. Is there a "scala" way to do this?
例如,如果我有一个convert()可以抛出异常的方法,我想捕获该异常,记录它并继续迭代。有没有“scala”的方式来做到这一点?
Ideally, I'd like something like...
理想情况下,我想要类似...
val points: Seq[Point] = ...
val convertedPoints: Seq[ConvertedPoint] = points.map(
p => {
try { p.convert() }
catch { case ex: Exception => logger.error("Could not convert", ex) }
})
You can't do the above code since it's not a direct mapping from one list to the other (you get back Seq[Any]as opposed to Seq[ConvertedPoint]).
您不能执行上述代码,因为它不是从一个列表到另一个列表的直接映射(您返回Seq[Any]而不是Seq[ConvertedPoint])。
回答by Daniel C. Sobral
Interesting that I had a lot of trouble explaining the advantages of using scala.util.control.Exceptionover try/catch, and then I start to see questions that make perfect examples out of them.
有趣的是,我在解释使用scala.util.control.Exceptionover try/的优点时遇到了很多麻烦catch,然后我开始看到一些问题,可以从中找到完美的例子。
Here:
这里:
import scala.util.control.Exception._
List(1, 23, 5, 2, 0, 3, 2) flatMap (x => catching(classOf[Exception]) opt (10 / x))
Your own code would look like this:
您自己的代码如下所示:
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)
)
Or, if you refactor it:
或者,如果你重构它:
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)))
回答by DaGGeRRz
flatMap is probably what you're looking for, but the map function has logging side-effect and these side-effects may not occur immediately if points were a view:
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")
This would print:
这将打印:
Conversion complete
[Error messages]
x were converted correctly
In your case, drop the view and you're probably fine. :)
在您的情况下,删除视图,您可能会没事。:)
To make the conversion a pure function (no side-effects), you'd probably use Either. Although I don't think it's worth the effort here (unless you actually want to do something with the errors), here's a pretty complete example of using it:
要使转换成为纯函数(无副作用),您可能会使用任何一种。虽然我不认为在这里付出努力是值得的(除非你真的想对错误做一些事情),但这里有一个非常完整的使用它的例子:
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(","))
回答by DaGGeRRz
Perhaps you want a flatMap. Here is an example, should see how it can fit :-)
也许你想要一个 flatMap。这是一个例子,应该看看它如何适合:-)
List(1,2,3,4).flatMap(x => if (x > 2) Some(x) else None)
The above would use side-effect logging (printing or putting in something mutable -- if this is done, make sure the evaluation is forced!). To get around the side-effects and caveats, the mapping function could just be of Point -> Either[CovertedPoint,Exception]and then the results can be separated with Seq.partitionor similar.
以上将使用副作用日志记录(打印或放入可变的东西——如果这样做,请确保评估是强制的!)。为了避免副作用和警告,映射函数可以只是 of Point -> Either[CovertedPoint,Exception],然后结果可以与Seq.partition或相似的分开。
回答by Xavier Guihot
Starting Scala 2.10you could use Tryto catch exceptions with flatMapto get rid of these and starting Scala 2.13couple Trywith tapto log exceptions:
开始Scala 2.10你可以使用Try捕捉异常与flatMap摆脱这些,并开始Scala 2.13对夫妇Try与tap记录异常:
List("34", "a", "1", "3", "1l")
.flatMap(x => Try(x.toInt).tap(_.failed.foreach(println)).toOption)
// java.lang.NumberFormatException: For input string: "a"
// java.lang.NumberFormatException: For input string: "1l"
// res0: List[Int] = List(34, 1, 3)
This:
这:
maps each element to anInt(our example of a code generating an exception) safe guarded by wrapping it in aTry. This will produce aTry[Int]which is either aFailure(NumberFormatException)or aSuccess(12).These
Trys aretapped toprint(or log) the error for theFailures.tapapplies a side effect (in this case some logging) on any value while returning the original value, so the returned value oftapis the element it's applied on, i.e. the unmodifiedTry.We then transform the
Trys intoOptions (Success(12)becomesSome(12)andFailure(NumberFormatException)becomesNone) in order to apply aflatMapwhich get rids ofNones (Failures) and extract values (12fromSome(12)(Success(12))).
map通过将每个元素Int包装在一个Try. 这将产生 aTry[Int],它是 aFailure(NumberFormatException)或 aSuccess(12)。这些
Trys 被tapped 到print(或记录)Failures的错误。tap在返回原始值的同时对任何值应用副作用(在这种情况下是一些日志记录),因此返回的值tap是它应用到的元素,即未修改的Try.然后我们将
Trys 转换为Options(Success(12)变成Some(12)和Failure(NumberFormatException)变成None),以便应用 aflatMap去除Nones(Failures)并提取值(12fromSome(12)(Success(12)) )。
Before Scala 2.13, an equivalent version without tapcould be:
在 之前Scala 2.13,没有的等效版本tap可能是:
List("34", "a", "1", "3", "1l")
.flatMap(x =>
(Try(x.toInt) match {
case f @ Failure(e) => {
println(e)
f
}
case s => s
}).toOption
)

![Scala 模式匹配与 Option[Any] 混淆](/res/img/loading.gif)