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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 02:32:49  来源:igfitidea点击:

Catching an exception within a map

scalaloopsdictionaryexceptionscala-collections

提问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对夫妇Trytap记录异常:

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 an Int(our example of a code generating an exception) safe guarded by wrapping it in a Try. This will produce a Try[Int]which is either a Failure(NumberFormatException)or a Success(12).

  • These Trys are tapped to print(or log) the error for the Failures. tapapplies a side effect (in this case some logging) on any value while returning the original value, so the returned value of tapis the element it's applied on, i.e. the unmodified Try.

  • We then transform the Trys into Options (Success(12)becomes Some(12)and Failure(NumberFormatException)becomes None) in order to apply a flatMapwhich get rids of Nones (Failures) and extract values (12from Some(12)(Success(12))).

  • map通过将每个元素Int包装在一个Try. 这将产生 a Try[Int],它是 aFailure(NumberFormatException)或 a Success(12)

  • 这些Trys 被tapped 到print(或记录)Failures的错误。tap在返回原始值的同时对任何值应用副作用(在这种情况下是一些日志记录),因此返回的值tap是它应用到的元素,即未修改的Try.

  • 然后我们将Trys 转换为Options(Success(12)变成Some(12)Failure(NumberFormatException)变成None),以便应用 aflatMap去除Nones(Failures)并提取值(12from Some(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
  )