La récursivité est un sujet délicat à comprendre et je ne pense pas pouvoir lui rendre pleinement justice ici. Au lieu de cela, je vais essayer de me concentrer sur le morceau de code particulier que vous avez ici et d'essayer de décrire à la fois l'intuition de savoir pourquoi la solution fonctionne et les mécanismes de la façon dont le code calcule son résultat.
Le code que vous avez donné ici résout le problème suivant: vous voulez connaître la somme de tous les entiers de a à b, inclus. Pour votre exemple, vous voulez la somme des nombres de 2 à 5, inclus, qui est
2 + 3 + 4 + 5
Lorsque vous essayez de résoudre un problème de manière récursive, l'une des premières étapes devrait être de déterminer comment décomposer le problème en un problème plus petit avec la même structure. Supposons donc que vous vouliez additionner les nombres de 2 à 5 inclus. Une façon de simplifier cela est de noter que la somme ci-dessus peut être réécrite comme
2 + (3 + 4 + 5)
Ici, (3 + 4 + 5) se trouve être la somme de tous les nombres entiers entre 3 et 5 inclus. En d'autres termes, si vous voulez connaître la somme de tous les entiers entre 2 et 5, commencez par calculer la somme de tous les entiers entre 3 et 5, puis ajoutez 2.
Alors, comment calculez-vous la somme de tous les nombres entiers entre 3 et 5 inclus? Eh bien, cette somme est
3 + 4 + 5
qui peut être considéré à la place comme
3 + (4 + 5)
Ici, (4 + 5) est la somme de tous les entiers compris entre 4 et 5 inclus. Donc, si vous vouliez calculer la somme de tous les nombres entre 3 et 5 inclus, vous calculeriez la somme de tous les nombres entiers entre 4 et 5, puis ajoutez 3.
Il y a un modèle ici! Si vous souhaitez calculer la somme des entiers entre a et b, inclus, vous pouvez effectuer les opérations suivantes. Tout d'abord, calculez la somme des entiers entre a + 1 et b, inclus. Ensuite, ajoutez un à ce total. Vous remarquerez que «calculer la somme des entiers entre a + 1 et b, inclus» se trouve être à peu près le même genre de problème que nous essayons déjà de résoudre, mais avec des paramètres légèrement différents. Plutôt que de calculer de a à b inclusivement, nous calculons de a + 1 à b inclus. C'est l'étape récursive - pour résoudre le plus gros problème ("somme de a à b, inclus"), nous réduisons le problème à une version plus petite de lui-même ("somme de a + 1 à b, inclus.").
Si vous regardez le code que vous avez ci-dessus, vous remarquerez qu'il y a cette étape dedans:
return a + sumInts(a + 1, b: b)
Ce code est simplement une traduction de la logique ci-dessus - si vous voulez faire la somme de a à b, inclus, commencez par additionner a + 1 à b, inclus (c'est l'appel récursif à sumInt
s), puis ajoutez a
.
Bien sûr, cette approche ne fonctionnera pas en soi. Par exemple, comment calculeriez-vous la somme de tous les nombres entiers entre 5 et 5 inclus? Eh bien, en utilisant notre logique actuelle, vous calculeriez la somme de tous les nombres entiers entre 6 et 5 inclusivement, puis additionneriez 5. Alors, comment calculez-vous la somme de tous les nombres entiers entre 6 et 5 inclus? Eh bien, en utilisant notre logique actuelle, vous calculeriez la somme de tous les nombres entiers entre 7 et 5 inclusivement, puis ajoutez 6. Vous remarquerez un problème ici - cela continue et continue!
Dans la résolution de problèmes récursive, il doit y avoir un moyen d'arrêter de simplifier le problème et d'aller le résoudre directement. En règle générale, vous trouverez un cas simple où la réponse peut être déterminée immédiatement, puis structurez votre solution pour résoudre des cas simples directement lorsqu'ils surviennent. Ceci est généralement appelé un cas de base ou une base récursive .
Alors, quel est le cas de base dans ce problème particulier? Lorsque vous additionnez des entiers de a à b, inclus, si a est plus grand que b, alors la réponse est 0 - il n'y a pas de nombres dans la plage! Par conséquent, nous allons structurer notre solution comme suit:
- Si a> b, alors la réponse est 0.
- Sinon (a ≤ b), obtenez la réponse comme suit:
- Calculez la somme des entiers entre a + 1 et b.
- Ajoutez un pour obtenir la réponse.
Maintenant, comparez ce pseudocode à votre code réel:
func sumInts(a: Int, b: Int) -> Int {
if (a > b) {
return 0
} else {
return a + sumInts(a + 1, b: b)
}
}
Notez qu'il y a presque exactement une carte un-à-un entre la solution décrite dans le pseudocode et ce code réel. La première étape est le cas de base - dans le cas où vous demandez la somme d'une plage vide de nombres, vous obtenez 0. Sinon, calculez la somme entre a + 1 et b, puis ajoutez a.
Jusqu'à présent, je n'ai donné qu'une idée de haut niveau derrière le code. Mais vous aviez deux autres très bonnes questions. Premièrement, pourquoi cela ne renvoie-t-il pas toujours 0, étant donné que la fonction dit de retourner 0 si a> b? Deuxièmement, d'où viennent les 14? Regardons-les à tour de rôle.
Essayons un cas très, très simple. Que se passe-t-il si vous appelez sumInts(6, 5)
? Dans ce cas, en suivant le code, vous voyez que la fonction renvoie simplement 0. C'est la bonne chose à faire, pour - il n'y a pas de nombres dans la plage. Maintenant, essayez quelque chose de plus difficile. Que se passe-t-il lorsque vous appelez sumInts(5, 5)
? Eh bien, voici ce qui se passe:
- Vous appelez
sumInts(5, 5)
. Nous tombons dans la else
branche, qui renvoie la valeur de `a + sumInts (6, 5).
- Afin
sumInts(5, 5)
de déterminer ce que sumInts(6, 5)
c'est, nous devons mettre en pause ce que nous faisons et passer un appel sumInts(6, 5)
.
sumInts(6, 5)
est appelé. Il entre dans la if
succursale et revient 0
. Cependant, cette instance de a sumInts
été appelée par sumInts(5, 5)
, donc la valeur de retour est communiquée à sumInts(5, 5)
, et non à l'appelant de niveau supérieur.
sumInts(5, 5)
maintenant peut calculer 5 + sumInts(6, 5)
pour revenir 5
. Il le renvoie ensuite à l'appelant de niveau supérieur.
Remarquez comment la valeur 5 a été formée ici. Nous avons commencé avec un appel actif à sumInts
. Cela a déclenché un autre appel récursif et la valeur retournée par cet appel a communiqué les informations à sumInts(5, 5)
. L'appel à sumInts(5, 5)
alors à son tour a effectué un certain calcul et renvoyé une valeur à l'appelant.
Si vous essayez ceci avec sumInts(4, 5)
, voici ce qui va se passer:
sumInts(4, 5)
essaie de revenir 4 + sumInts(5, 5)
. Pour ce faire, il appelle sumInts(5, 5)
.
sumInts(5, 5)
essaie de revenir 5 + sumInts(6, 5)
. Pour ce faire, il appelle sumInts(6, 5)
.
sumInts(6, 5)
renvoie 0 à sumInts(5, 5).</li>
<li>
sumInts (5, 5) now has a value for
sumInts (6, 5) , namely 0. It then returns
5 + 0 = 5`.
sumInts(4, 5)
a maintenant une valeur pour sumInts(5, 5)
, à savoir 5. Il retourne ensuite 4 + 5 = 9
.
En d'autres termes, la valeur renvoyée est formée en additionnant les valeurs une par une, en prenant à chaque fois une valeur renvoyée par un appel récursif particulier à sumInts
et en ajoutant la valeur actuelle de a
. Lorsque la récursion atteint son maximum, l'appel le plus profond renvoie 0. Cependant, cette valeur ne quitte pas immédiatement la chaîne d'appels récursifs; au lieu de cela, il remet simplement la valeur à l'appel récursif un calque au-dessus. De cette façon, chaque appel récursif ajoute simplement un nombre supplémentaire et le renvoie plus haut dans la chaîne, aboutissant à la sommation globale. En tant qu'exercice, essayez de tracer cela sumInts(2, 5)
, c'est ce que vous vouliez commencer.
J'espère que cela t'aides!