Подтвердить что ты не робот

Scala - Захват исключения внутри карты

Каков наилучший способ обработки исключений при повторении цикла в 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 [Any] в отличие от Seq [ConvertedPoint]). Любая помощь будет принята с благодарностью!

Спасибо!

4b9b3361

Ответ 1

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

В вашем случае отбросьте представление, и вы, вероятно, в порядке.:)

Чтобы сделать преобразование чистой функцией (без побочных эффектов), вы, вероятно, будете использовать Либо. Хотя я не думаю, что это стоит усилий здесь (если вы действительно не хотите что-то делать с ошибками), вот довольно полный пример его использования:

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

Ответ 2

Интересно, что у меня было много проблем, объясняющих преимущества использования 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)))

Ответ 3

Возможно, вам нужна плоская карта. Вот пример, должен увидеть, как он может поместиться: -)

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

Вышеупомянутое будет использовать ведение журнала побочных эффектов (печать или вставка чего-либо изменчивого - если это будет сделано, убедитесь, что оценка принудительно!). Чтобы обойти побочные эффекты и оговорки, функция сопоставления может быть только Point -> Either[CovertedPoint,Exception], а затем результаты могут быть разделены на Seq.partition или аналогичные.