Y a-t-il une façon fonctionnelle / scala d'appeler une fonction à plusieurs reprises jusqu'à ce qu'elle réussisse, tout en réagissant aux tentatives infructueuses?
Permettez-moi d'illustrer avec un exemple. Supposons que je souhaite lire un entier de la norme-IN et réessayer si l'utilisateur n'entraînait pas en integile. P>
donné cette fonction: p> Et ces fonctions anonymes: p> Je les utiliserais comme ceci: p> Mes questions sont: p > merci! p> *) à part le serpent_case. Je vraiment em> comme ça. P> p>
retry_until_right code> existe déjà dans SCALA? LI>
5 Réponses :
C'était ma première implémentation récursive de la queue: mais désirant réutiliser les bibliothèques existantes, j'ai ensuite changé sur cette approche à l'aide d'itérateurs: P> val num = Iterator.continually(ask_for_int()).onLeft(handle_not_int).firstRight
println("Thanks! You entered: " + num)
Le Toseq code> ne produira-t-il pas une collection infinie? Peut-être que vous voulez un bufferéditerator pour que vous puissiez avoir la tête?
@Phaises - Je pense que ça ne le fait pas. Tolist semble forcer la collection, pas Toseq. Mais je pourrais me tromper. Au moins je l'ai essayé et ça marche.
Notez également qu'il ne peut pas réussir à produire une collection infinie, car cela dépend de l'entrée de Ask_for_int (). Cela doit attendre. Cela entraînerait une boucle infinie si ask_for_int () n'a jamais retourné un int. Mais c'est l'idée de toute façon.
def retry[L, R](f: => Either[L, R])(handler: L => Any): R = { val e = f e.fold(l => { handler(l); retry(f)(handler) }, identity) }
Utilisation très intéressante du pliage et en particulier identité code>. Était nouveau pour moi.
La fonction d'identité est une chose très naturelle à utiliser lors de la pliage avec des fonctions @sebastiann. . De la même manière que 0 est la graine naturelle lors du pliage d'une somme et 1 lors du pliage d'un produit, l'identité est la graine naturelle où un pli construirait une fonction cumulative. Si la collection est vide, vous recevez une fonction qui renvoie simplement son argument. Sinon, vous recevez une fonction itérative qui transforme l'argument (avec une identité étant une étape initiale ou finale inoffensive). Utile avec les collections, l'option, soit des autres.
Je pense que le alors vous pouvez faire: p> ou vous pouvez même masquer le alors vous pouvez simplement: p> ou p> essayer code> monad avec le
itérateur.Continalement code> est adapté à ce problème général. Bien sûr, cette réponse pourrait être adoptée pour utiliser
code> si vous êtes tellement enclin:
essayer code> une partie de la mise en œuvre et donner
oncwrong code> une valeur par défaut et en faire un second paramètre au lieu d'une fonction curry: p>
Je vois que votre approche est similaire à la mienne (continuellement / platmap / toseq / tête). Heureux de voir que j'étais sur la bonne voie. Il semble donc que cela ne soit pas intégré. Peut-être que Scalaz, informe ou certaines d'entre elles ont quelque chose comme ça ...
Voici une solution alternative utilisant SCALAZ.CONCURRENT.TASK:
def retry[A](f: Task[A])(onError: PartialFunction[Throwable, Task[_]]): Task[A] = f handleWith (onError andThen (_.flatMap(_ => retry(f)(onError)))) val rawReadInt: Task[Int] = Task.delay(scala.io.StdIn.readLine().trim().toInt) val readInt: Task[Int] = retry(rawReadInt) { case e: java.lang.NumberFormatException => Task.delay(println("Failure!")) }
Désolé ... Newbie ici ... Qu'est-ce qu'un "trampoline" dans ce contexte?
Le trampoline est une stratégie pour éliminer les débordements de pile; Il représente le calcul hiérarchique sous forme de structure de données au lieu d'un ensemble de cadres de pile. Le "trampoline" est alors un cadre d'exécution qui peut traiter cette structure de données étape par étape jusqu'à ce que le résultat final soit évalué. De l'scaladoc pour ScalAck pour Scalaz.ConCurrent.Future: "L'avenir est un calcul trampoliné produisant un A qui peut inclure des marches asynchrones. Comme le trampoline, des expressions monadiques arbitraires impliquant une carte et une colonne graphique sont garanties pour utiliser un espace de pile constant." Voir aussi: "Programmation fonctionnelle à Scala", ch. 13
Peut-être une meilleure référence est le papier, "Stackless Scala avec des monades libres", de Rúnar Óli Bjarnason: blog.higher-order.com/assets/trampolines.pdf
Une mise en œuvre d'une nouvelle monade peut être trouvée ici: https://github.com/hipjim/scala-retry Il a diverses stratégies de réessayes.
// define the retry strategy implicit val retryStrategy = RetryStrategy.fixedBackOff(retryDuration = 1.seconds, maxAttempts = 2) // pattern match the result val r = Retry(1 / 1) match { case Success(x) => x case Failure(t) => log("I got 99 problems but you won't be one", t) }
duplicaté possible de Quel est le scala façon de mettre en œuvre un appel à nouveau capable comme celui-ci?
Je ne pense pas que ma question soit une duplicate de celui-là. Celui-ci est spécifique à l'échec par exception et la quantité de tentatives est numérotée. Ma question est plus abstraite. L'autre question pourrait être un cas particulier de mien. Cette question est également "quelle est la meilleure façon de le mettre en œuvre?", Est "Dois-je la mettre en œuvre?" (Ou existe-t-il déjà?)