Scala-Capturando una excepción dentro de un mapa
exception-handling loops (3)
Es interesante que tuve muchos problemas para explicar las ventajas de usar scala.util.control.Exception
sobre try
/ catch
, y luego comencé a ver preguntas que son ejemplos perfectos de ellas.
Aquí:
import scala.util.control.Exception._
List(1, 23, 5, 2, 0, 3, 2) flatMap (x => catching(classOf[Exception]) opt (10 / x))
Tu propio código se vería así:
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)
)
O, si lo refactoriza:
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)))
¿Cuál es la mejor forma de manejar excepciones al iterar sobre un bucle en Scala? Por ejemplo, si tuviera un método convert () que arrojara una excepción, me gustaría capturar esa excepción, registrarla y seguir iterando. ¿Hay una forma "scala" de hacer esto?
Idealmente, me gustaría algo como ...
val points: Seq[Point] = ...
val convertedPoints: Seq[ConvertedPoint] = points.map(
p => {
try { p.convert() }
catch { case ex: Exception => logger.error("Could not convert", ex) }
})
No puede hacer el código anterior, ya que no se trata de una asignación directa de una lista a otra (se obtiene Seq [Cualquiera] en lugar de Seq [ConvertedPoint]). ¡Cualquier ayuda sería muy apreciada!
¡Gracias!
Tal vez quieras un flatMap. Aquí hay un ejemplo, debería ver cómo puede caber :-)
List(1,2,3,4).flatMap(x => if (x > 2) Some(x) else None)
Lo anterior usaría el registro de efectos secundarios (imprimiendo o colocando algo mutable; si esto se hace, ¡asegúrese de que la evaluación sea forzada!). Para evitar los efectos secundarios y las advertencias, la función de mapeo podría ser de Point -> Either[CovertedPoint,Exception]
y luego los resultados se pueden separar con Seq.partition
o similar.
flatMap es probablemente lo que está buscando, pero la función de mapa tiene efecto secundario de registro y estos efectos secundarios pueden no ocurrir inmediatamente si los puntos fueron una vista:
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")
Esto se imprimiría:
Conversion complete
[Error messages]
x were converted correctly
En tu caso, deja caer la vista y probablemente estés bien. :)
Para que la conversión sea una función pura (sin efectos secundarios), probablemente usarías Cualquiera. Aunque no creo que valga la pena el esfuerzo aquí (a menos que realmente desee hacer algo con los errores), aquí hay un ejemplo bastante completo de su uso:
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(","))