web-dev-qa-db-fra.com

Comment revenez-vous d'une fonction tôt à Clojure?

LISP commun a return-from; Existe-t-il une sorte de return dans Clojure lorsque vous souhaitez revenir plus tôt après une fonction?

35
compman

Il n'y a pas d'instruction de retour explicite dans clojure. Vous pouvez pirater quelque chose ensemble en utilisant une combinaison catch/throw si vous le souhaitez, mais comme clojure est beaucoup plus fonctionnel que LISP commun, les chances que vous ayez besoinun retour rapide au beau milieu d'un bloc imbriqué sont beaucoup plus petites La seule "bonne" raison que je vois pour les instructions return est lorsque vous utilisez des objets mutables d’une manière qui n’est pas idiomatique en clojure.

Je n'irais pas jusqu'à dire que ce n'est jamais utile, mais je pense que dans Clojure, si votre algorithme a besoin d'une instruction return, il s'agit d'une odeur majeure de code.

23
Joost Diepenmaat

Lorsque vous avez besoin de sortir d'un calcul plus tôt, vous avez besoin d'un moyen de le faire, pas d'un argument de puristes. Généralement, vous en avez besoin lorsque vous réduisez une grande collection et qu'une certaine valeur indique qu'il est inutile de poursuivre le traitement de la collection. À cette fin, Clojure, toujours pratique, fournit la fonction reduced .

Un exemple simple à illustrer est que lors de la multiplication d'une séquence de nombres, si vous rencontrez un zéro, vous savez déjà que le résultat final sera zéro, vous n'avez donc pas besoin de regarder le reste de la séquence. Voici comment vous codez cela avec reduced:

(defn product [nums]
  (reduce #(if (zero? %2)
               (reduced 0.0)
               (* %1 %2))
          1.0
          nums))

reduced encapsule la valeur que vous lui attribuez dans une structure de données sentinel afin que reduce sache arrêter de lire dans la collection et renvoie simplement la valeur reduced maintenant. Hé, c'est purement fonctionnel, même!

Vous pouvez voir ce qui se passe si vous enveloppez la if ci-dessus dans une do avec un (println %1 %2):

user=> (product [21.0 22.0 0.0 23.0 24.0 25.0 26.0])
1.0 21.0
21.0 22.0
462.0 0.0
0.0

user=> (product [21.0 22.0 23.0 24.0 25.0 26.0])
1.0 21.0
21.0 22.0
462.0 23.0
10626.0 24.0
255024.0 25.0
6375600.0 26.0
1.657656E8
28
Ben Kovitz

Sauf si vous écrivez du code vraiment génial, la seule raison pour laquelle vous voudrez revenir plus tôt est si certaines conditions sont remplies. Mais puisque les fonctions renvoient toujours le résultat du dernier formulaire évalué, if est déjà cette fonction - il suffit de mettre la valeur que vous voulez renvoyer dans le corps de if et elle renverra cette valeur si la condition est remplie.

17
Chuck

Je ne suis pas un expert en Clojure, mais il semble que ce n’est pas le cas pour essayer d’être plus fonctionnel. Regardez ce que Stuart Halloway dit ici :

Common LISP prend également en charge la macro retour-depuis pour "revenir" depuis le milieu D'une fonction. Ceci encourage un style impératif de programmation , Ce que décourage Clojure.

Cependant, vous pouvez résoudre les mêmes problèmes d’une manière différente. Voici L'exemple return-from, récrit dans un style fonctionnel afin qu'aucun Return-from ne soit nécessaire:

(defn pair-with-product-greater-than [n]
 (take 1 (for [i (range 10) j (range 10) :when (> (* i j) n)] [i j])))

Autrement dit, utilisez des séquences paresseuses et renvoyez des valeurs en fonction de conditions.

14
Diego Sevilla

Il n'y a pas de déclaration de retour dans Clojure. Même si vous choisissez de ne pas exécuter de code à l'aide d'une construction de flux telle que if ou when, la fonction renverra toujours quelque chose, dans ces cas nil. Le seul moyen de sortir est de lancer une exception, mais même dans ce cas, cela fera une bulle et tuera votre thread, ou sera attrapé par une fonction - qui retournera une valeur.

0
Conan

L'option if déjà donnée est probablement le meilleur choix. Notez que, comme les cartes sont faciles à utiliser, vous pouvez toujours renvoyer {:error "blah"} dans la condition d'erreur, et{result: x} dans la condition valide.

0
Dax Fohl