La récursivité et le tri Fonction récursive ● Il est légal qu'une fonction en appelle une autre ; il est également légal pour une fonction de s'appeler elle-même. C'est l'une des choses les plus magiques qu'un programme puisse faire. Par exemple, regardez la fonction suivante : def countdown(n): if n <= 0: print('Blastoff!') else: print(n) countdown(n-1) Si n est 0 ou négatif, il affiche le mot "Blastoff!" Sinon, il affiche n puis appelle une fonction nommée countdown - elle-même - en passant n-1 comme argument. Que se passe-t-il si nous appelons cette fonction comme ceci ? Fonction récursive >>> countdown(3) L'exécution du countdown commence avec n=3, et comme n est supérieur à 0, il affiche la valeur 3, puis s'appelle… L'exécution du countdown commence avec n=2, et comme n est supérieur à 0, il sort la valeur 2, puis s'appelle… L'exécution du countdown commence avec n=1, et comme n est supérieur à 0, il affiche la valeur 1, puis s'appelle lui-même.. L'exécution du countdown commence avec n=0, et comme n n'est pas supérieur à 0, il affiche le mot « Blastoff ! » puis revient. Le countdown qui a obtenu n=1 revient. Le countdown qui a obtenu n=2 revient. Le countdown qui a obtenu n=3 revient. Fonction récursive ● Et puis vous êtes de retour dans __main__.(le programme principal) ● Ainsi, la sortie totale ressemble à ceci : 3 2 1 Blastoff Fonction récursive Diagrammes de pile pour les fonctions récursives Chaque fois qu'une fonction est appelée, Python crée un cadre pour contenir les variables locales et les paramètres de la fonction. ● Pour une fonction récursive, il peut y avoir plus d'une image sur la pile en même temps. Fonction récursive ● ● Comme d'habitude, le haut de la pile est le cadre de __main__. Il est vide car nous n'avons pas créé de variables dans __main__ ni passé d'arguments. Les quatre cadres de countdown ont des valeurs différentes pour le paramètre n. Le bas de la pile, où n = 0, est appelé le cas de base. Il ne fait pas d'appel récursif, il n'y a donc plus de cadres. Récursivité infinie ● ● ● Si une récursivité n'atteint jamais un cas de base, elle continue à faire des appels récursifs indéfiniment et le programme ne se termine jamais. C'est ce qu'on appelle la récursivité infinie. Voici un programme minimal avec une récursivité infinie : def recurse() : recurse() Si vous écrivez une récursivité infinie par accident, passez en revue votre fonction pour confirmer qu'il existe un cas de base qui ne fait pas d'appel récursif. Et s'il existe un cas de base, vérifiez si vous êtes assuré de l'atteindre. Fonction récursive Récursivité terminale et récursivité non terminale ● ● On peut différencier deux algorithmes de récursivité : les fonctions récursives pour lesquelles il n'y a aucun traitement entre l'appel récursif et le retour de la fonction, et celles pour lesquelles il y a des opérations entre l'appel et le retour. Exemple def S(n1,n2) : if n1 > n2 return 0 else : return S(n1,n2-1) + n2 pour calculer S(1, 4), on aura le schéma suivant : Fonction récursive ● Comme les opérations extérieures à l'appel récursif ne sont pas traitées au fur et à mesure, on conçoit bien que cet empilage/dépilage n'est pas très performant. Considérons maintenant la variante suivante def S(n1,n2,res=0) if n1 > n2 : return res return S(n1,n2-1,res+n2) Ici, le retour est directement l'appel récursif, il n' y aucune tâche à traiter. Dans cette situation,le schéma d'exécution sera le suivant pour le calcul de S( 1,4,0). ● D'un point de vue théorique, on voit qu'on gagne donc en rapidité d'exécution. Ce dernier type de fonctions récursives, avec retour direct de l'appel récursif s'appelle récursivité terminale. Fonction récursive TD Exercice 1: Quelle est la sortie du programme suivant ? Dessinez un diagramme de pile qui montre l'état du programme lorsqu'il imprime le résultat. Que se passerait-il si vous appeliez cette fonction ainsi : recurse(-1, 0) ? Fonction récursive TD Exercice 2: ● Écrivez une fonction récursive appelée is_palindrome qui prend une chaîne en argument et renvoie True s'il s'agit d'un palindrome et False sinon. Exercice 3: ● ● Un nombre, a, est une puissance de b s'il est divisible par b et a/b est une puissance de b. Écrivez une fonction appelée is_power qui prend les paramètres a et b et renvoie True si a est une puissance de b. Remarque : vous devrez penser au cas de base. Exercice 4 : ● Écrivez une fonction appelée pgcd qui prend les paramètres a et b et renvoie leur plus grand diviseur commun. Une façon de trouver le PGCD est basée sur l'observation que si r est le reste quand a est divisé par b, alors pgcd (a, b) = pgcd (b, r) . Comme cas de base, nous pouvons utiliser pgcd (a, 0) = a. Fonction récursive TD Exercice 5 Écrire une fonction puissance qui calcule la puissance entière d'un nombre. On pourra écrire une fonction récursive non terminale puis la transformer en une fonction récursive terminale.