Неоднократная функция вызова во время выполнения предиката
Я делаю запросы на удаленный сервер, а иногда запросы терпят неудачу из-за ненадежной сети. В случае неудачи я хочу, чтобы запрос повторялся, но n
times at max. Если бы я использовал императивный язык, я бы поставил код отправки запроса в цикл while, но я хочу сделать это функциональным способом.
Я написал для этого помощника:
/** Repeatedly executes function `f`
* while predicate `p` holds
* but no more than `nTries` times.
*/
def repeatWhile[A](f: => A)(p: A => Boolean)(nTries: Int): Option[A] =
if (nTries == 0) {
None
} else {
f match {
case a if p(a) => repeatWhile(f)(p)(nTries - 1)
case a => Some(a)
}
}
И используя его вот так:
// Emulating unreliable connection
var n = 0
def receive(): Option[String] =
if (n < 4) {
n += 1
println("No result...")
None
} else {
println("Result!")
Some("Result")
}
// Repeated call
val result = repeatWhile(receive)(!_.isDefined)(10)
где receive
- глупая функция для целей тестирования. Этот код выполняет 4 вызова до того, как receive
, наконец, успешно завершит работу с Some(Result)
:
No result...
No result...
No result...
No result...
Result!
My repeatWhile
отлично работает, но я чувствую, как изобретать колесо. Я изучаю функциональное программирование и хочу знать, есть ли простые/стандартные решения моей проблемы.
P.s. Я определил еще больше помощников, возможно, они уже находятся в языковой/стандартной библиотеке?
/** Repeatedly executes function `f`
* while predicated `p` not holds
* but no more than `nTries` times.
*/
def repeatWhileNot[A](f: => A)(p: A => Boolean)(nTries:Int): Option[A] =
repeatWhile(f)(!p(_))(nTries)
/** Repeatedly executes function `f`
* while it returns None
* but no more than `nTries` times.
*/
def repeatWhileNone[A](f: => Option[A])(nTries:Int): Option[A] =
repeatWhileNot(f)(_.isDefined)(nTries).getOrElse(None)
Ответы
Ответ 1
Канонический способ заключается в использовании Iterator
:
Iterator.continually{f}.take(nTries).dropWhile(!p).take(1).toList
который даст вам либо пустой список, либо список из одного элемента, в зависимости от того, был ли он успешным. Вы можете преобразовать это в опцию с помощью headOption
, если хотите. С небольшими изменениями это работает во всех ваших случаях использования.
Написание небольших рекурсивных методов, как вы это сделали, совершенно разумно, хотя они и не находятся в библиотеке. В общем, написание вспомогательных методов для того, что вы делаете больше всего, - очень хорошая идея. Это одна из причин, почему Scala позволяет легко писать методы.