Рекурсия + функции первого класса параметры по имени == потрясающе.
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e =>
if (n > 1) retry(n - 1)(fn)
else throw e
}
}
Использование выглядит так:
retry(3) {
// insert code that may fail here
}
Редактировать : небольшое изменение, вдохновленное ответом @ themel . На одну строку кода меньше: -)
def retry[T](n: Int)(fn: => T): T = {
try {
fn
} catch {
case e if n > 1 =>
retry(n - 1)(fn)
}
}
Редактировать снова : рекурсия беспокоила меня тем, что она добавила несколько вызовов в трассировку стека. По какой-то причине компилятор не смог оптимизировать хвостовую рекурсию в обработчике catch. Хвостовая рекурсия не в обработчике catch, тем не менее, оптимизируется просто отлично: -)
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
val r = try { Some(fn) } catch { case e: Exception if n > 1 => None }
r match {
case Some(x) => x
case None => retry(n - 1)(fn)
}
}
Отредактируйте еще раз : Очевидно, я собираюсь сделать это хобби, чтобы продолжать возвращаться и добавлять альтернативы к этому ответу. Вот хвосто-рекурсивная версия, которая немного более проста, чем использование Option
, но использование return
для короткого замыкания функции не является идиоматическим Scala.
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
try {
return fn
} catch {
case e if n > 1 => // ignore
}
retry(n - 1)(fn)
}
Обновление Scala 2.10 . Как и мое хобби, я иногда возвращаюсь к этому ответу. Scala 2.10 в представленном виде Попробуйте , который обеспечивает чистый способ реализации повторных попыток с хвостовой рекурсией.
// Returning T, throwing the exception on failure
@annotation.tailrec
def retry[T](n: Int)(fn: => T): T = {
util.Try { fn } match {
case util.Success(x) => x
case _ if n > 1 => retry(n - 1)(fn)
case util.Failure(e) => throw e
}
}
// Returning a Try[T] wrapper
@annotation.tailrec
def retry[T](n: Int)(fn: => T): util.Try[T] = {
util.Try { fn } match {
case util.Failure(_) if n > 1 => retry(n - 1)(fn)
case fn => fn
}
}