2017-10-12 6 views
2

J'essaie d'apprendre à programmer avec le livre Clojure for the Brave and True (CFTBAT). A la fin du cours intensif, l'auteur nous fait écrire un petit programme pour illustrer le bouclage de Clojure. Pour expliquer la partie en boucle et récursive du programme, here, l'auteur écrit un exemple plus petit en utilisant loop et montre ensuite qu'il est possible de remplacer loop par une définition de fonction normale.Récursion avec Clojure et Clojure pour les Brave et Vrai

C'est cet exemple de définition de fonction normale que je ne comprends pas. Voici le code:

(defn recursive-printer 
    ([] 
     (recursive-printer 0)) 
    ([iteration] 
     (println iteration) 
     (if (> iteration 3) 
      (println "Bye!") 
      (recursive-printer (inc iteration))))) 

(recursive-printer) 

Je ne comprends pas le code parce que je ne vois pas où sont les arguments de la fonction recursive-printer. Dans Clojure, les arguments d'une fonction sont supposés être entre parenthèses et le corps entre parenthèses. Ainsi, dans cet exemple, les arguments seraient un argument vide [] et iteration. Mais alors pourquoi sont-ils mis entre parenthèses aussi?

Et qu'est-ce que (recursive-printer 0) Est-ce un appel de fonction, où la fonction s'appelle elle-même?

Si quelqu'un pouvait m'expliquer comment ce morceau de code fonctionne, ce serait très apprécié.

+4

Il existe deux listes d'arguments différentes. Un '[]' - avec le code pour gérer le cas où il est appelé sans arguments - et un '[itération]', pour gérer quand il est appelé avec un argument. –

+2

BTW, dans la mesure où ce livre est disponible en ligne, il peut ne pas blesser d'avoir un lien - la version web n'est pas orientée page par page, donc "page 63" n'aide pas les gens qui l'utilisent beaucoup . –

+5

Voir le chapitre 3 sur "Arity Overloading": https://www.braveclojure.com/do-things/ –

Répondre

2

En clojure, vous pouvez définir une fonction telle qu'elle puisse prendre différents nombres de arguments, par ex.

(defn foo [] 
    ....) 

est une fonction qui ne prend aucun argument. Il est appelé comme ceci ..

(foo) 

(defn foo [x] 
    ...) 

est une fonction qui prend 1 argument. Il peut être appelé comme

(foo :a) 

mais parfois, vous voudrez peut-être définir une fonction qui prend zéro ou 1 argument . Pour ce faire, vous utilisez un format légèrement différent

(defn foo 
    ([] ;no argument form 
    ...) 
    ([x] ;single argument form 
    ...)) 

Un idiome commun est d'utiliser une forme d'argument zéro dans une définition de fonction récursive et inclure un formulaire unique argument « accumulateur » comme la partie principale de la fonction .Alors, en regardant votre exemple, vous avez

(defn recursive-printer 
    ([] ; zero argument form 
     (recursive-printer 0)) 
    ([iteration] ; 1 argument form 
     (println iteration) 
     (if (> iteration 3) 
      (println "Bye!") 
      (recursive-printer (inc iteration))))) 

(recursive-printer) 

Lorsque vous appelez (imprimante récursive) appelle la première forme (zéro argument sous forme ). Cette forme appelle à son tour la fonction avec un seul argument de 0. Ce appelle le deuxième formulaire.

La deuxième forme imprime d'abord sur l'argument, teste ensuite pour voir si elle est supérieur à 3, qui, dans le premier appel, il est pas comme il est 0, il exécute le else déclaration, qui fait un appel récursif avec un nouvel argument qui est l'argument actuel augmenté de 1. Votre argument est maintenant 1 et il est imprimé sur. le test est encore faux car 1 n'est pas> 3, donc la fonction est appelée à nouveau avec l'argument augmenté de 1 soit 2. Dans cet appel, 2 est imprimé, le test est toujours pas plus que trois, donc la fonction est appelé à nouveau avec l'argument augmenté à 3. Dans cet appel, 3 est imprimé et le test n'est toujours pas> 3, donc l'argument est incrémenté à 4 et la fonction appelée à nouveau avec 4 comme l'argument . La valeur 4 est imprimée, mais cette fois 4 est> 3, donc la chaîne "Bye" est imprimé et comme c'est la condition de fin, aucun autre appel récursif n'est fait et la pile se déroule et la fonction se termine.

1

Nous pouvons laisser tomber le zéro arité:

(defn recursive-printer [iteration] 
    (println iteration) 
    (if (> iteration 3) 
    (println "Bye!") 
    (recursive-printer (inc iteration)))) 

... et appeler la fonction avec un argument 0 explicite:

(recursive-printer 0) 
0 
1 
2 
3 
4 
Bye! 
=> nil 

Cela nous permet de nous concentrer sur la récursion. Depuis l'appel récursif est la dernière chose que fait (en position de queue), on peut utiliser à la place recur:

(defn recursive-printer [iteration] 
    (println iteration) 
    (if (> iteration 3) 
    (println "Bye!") 
    (recur (inc iteration)))) 

... avec exactement le même effet.

L'arité supplémentaire confonde tout simplement les choses, je pense.

+0

Je pense que l'auteur voulait montrer un exemple de récursivité sans utiliser 'loop' ou' recur'. – guillaume8375