Как вывести индекс сбоя в выражении catch в Scala? - PullRequest
1 голос
/ 29 июня 2019

У меня есть код, похожий на этот:

  import scala.util.{Try, Success, Failure}
  Try(
    for (i <- 1 to 1000) {
      doSomething(df(i))
    }
  ) match {
    case Success(t) => println(s"success")
    case Failure(t)  => println(s"failure")

  }

Я хочу напечатать индекс неудачного ввода.как напечатать индекс i в выражении catch?

Ответы [ 3 ]

4 голосов
/ 29 июня 2019

Вы можете сделать это вместо этого, используя Кошки :

import scala.util.Try
import cats.implicits._

(1 to 1000).traverse(i => Try(doSomething(df(i))).toEither.left.map(ex => (ex, i))) match {
  case Right(_)      => println("success")
  case Left((ex, i)) => println(s"failure: ${ex.getMessage} on idx: ${i}")
}

Если вы не хотите использовать Кошки , вы можете просто:

val attempts = for {
  i <- Stream.range(start = 1, end = 1000) // Thanks to Bogdan for the idea of using a Stream.
} yield Try(doSomething(df(i))).toEither.left.map(ex => (ex, i))

attempts.collectFirst { case Left((ex, i)) => ex -> i } match {
  case None          => println("success")
  case Some((ex, i)) => println(s"failure: ${ex.getMessage} on idx: ${i}")
}
1 голос
/ 29 июня 2019

Вы обязательно должны следовать ответу Луиса, но чтобы ответить на ваш комментарий, вы также можете поймать IllegalArgumentException и перебросить его с добавленным индексом к сообщению, возможно, что-то вроде этого:

Try(
  for (i <- 1 to 1000) {
    try doSomething(i) catch { case e: IllegalArgumentException => throw new IllegalArgumentException(s"Failed with index $i", e)}
  }
) match {
  case Success(t) => println(s"success")
  case Failure(t)  => println(s"failure", t)
}

Однако это кажется отвратительным, и я не советую это.

0 голосов
/ 02 июля 2019

ИМО вопрос намекает на то, что код врет.
Вы можете написать код по-другому:

import scala.util.{Try, Success, Failure}
for (i <- 1 to 1000) {
  Try(
      doSomething(df(i))
  ) match {
    case Failure(t)  => println(s"failure on $i")
    case _ =>
  }
}

Но ты не хочешь. Почему бы и нет? Потому что вы хотите остановить итерацию после первого сбоя. Но вы используете цикл от 1 до 1000. На самом деле вы не собираетесь делать все 1000 итераций. Вы используете исключение, чтобы разорвать цикл for.

Я бы переписал этот код, чтобы было ясно, что я не собираюсь явно повторять весь диапазон.

Вы можете, например, использовать find вместо for, чтобы найти индекс, который вызывает сбой. если ничего не найдено -> все прошло успешно.

так что-то похожее (не проверено):

(1 to 1000).indexWhere{index=>Try{doSomething(index)}.isFailure

Я не уверен, найден ли он или где-то в Scala, но вы поняли.

если вы также хотите получить исключение, а не только индекс, вы можете использовать представления (https://docs.scala -lang.org / Overviews / collection / views.html ), чтобы изменить свою последовательность на лениво оцененный, сопоставьте список с кортежем формы (index, Try) (без итерации всей коллекции, из-за ленивости результата .view), а затем collectFirst, где вторым элементом кортежа является Failure.

так что-то вроде (не проверено):

(1 to 1000).view.map{index => (index, doSomething(index)}.collectFirst{case (i,Failure(e)) => println(s"error was $e at index $i")}

в качестве альтернативы вы могли бы написать очень маленькую рекурсию для итерации последовательности индекса (также не проверенной)

def findException(indexes: Seq[Int]): Option[(Int, Exception)] = indexes match {
   case Nil => None
   case index+:remaining => 
      Try(doSomething(i)) match {
         case Success(_) => findException(remaining)
         case Failure(e) => Option((index,e)) 
}
findException(1 to 1000).map(println)

один вопрос: как вы определили от 1 до 1000? этот вопрос выглядел бы иначе, если бы у вас был набор элементов для проверки, а не диапазон. в этом случае вы, вероятно, просто использовали бы foldLeft.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...