2010-11-03 22 views
12

¿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?Scala - Captura de una excepción dentro de un mapa

Idealmente, me gustaría algo así 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 se puede hacer que el código anterior ya que no es un mapeo directo de una lista a la otra (que vuelvas Sec [Cualquier] a diferencia de Seq [ConvertedPoint]). ¡Cualquier ayuda sería muy apreciada!

Gracias!

Respuesta

5

Quizás quieras un flatMap. Aquí está un ejemplo, debería ver cómo se puede encajar :-)

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

Lo anterior utilizaría el registro de efectos secundarios (impresión o poner en algo mutable - si se hace esto, asegúrese de que la evaluación se ve obligado!) 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.

14

flatMap es probablemente lo que usted está buscando, pero la función de mapa tiene el registro de efectos secundarios y estos efectos secundarios no puede ocurrir inmediatamente si los puntos fueron vistas:

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 imprimiría:

Conversion complete 
[Error messages] 
x were converted correctly 

En su caso, suelte la vista y probablemente esté bien. :)

Para que la conversión sea una función pura (sin efectos secundarios), probablemente usaría Cualquiera. Aunque no creo que vale la pena el esfuerzo aquí (a menos que realmente quiere hacer algo con los errores), aquí hay un ejemplo bastante completa de usarlo:

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

Me robaste la respuesta, pero para 1 Explicándolo mejor :-) Bienvenido a SO. –

+1

Estoy de acuerdo: su respuesta fue 100% correcta (y una inspiración), pero creo que agregar más detalles justificó otra respuesta en lugar de solo un comentario. :) – DaGGeRRz

12

interesante que tenía un montón de problemas para explicar la ventajas de usar scala.util.control.Exception sobre try/catch, y luego empiezo a ver preguntas que los convierten en ejemplos perfectos.

aquí:

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

su 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 refactorearlo:

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