Algorithmes et fonctions
Le pseudo-code permet d'écrire des algorithmes de façon claire et sans ambiguïtés.
Algorithme
Définition
Un algorithme est une suite d'instructions de pseudo-code qui, a partir de paramètres d'entrées de type déterminé, rend une sortie de type déterminé.
Considérons par exemple la description de l'algorithme de recherche d'un élément dans une liste :
Nom : recherche
Entrées :
T : un tableau d'entiers
x : un entier
Sortie :
un booléen
Programme :
parcourir chaque élément de t jusqu'à trouver un élément dont la valeur est égale à la valeur de x.
Si on trouve un tel élément, rendre "Vrai"
Sinon rendre "Faux"
En pseudo-code, cela va donner ça :
algorithme recherche(T: [entier],
x: entier # entier recherché dans t
) → booléen: # Vrai si x est dans t
e := entier
pour chaque e de T:
si e == x:
rendre Vrai
rendre Faux
Le programme est un bloc. La definition du bloc (jusqu'aux :) est constitué :
- du mot-clé
algorithmequi détermine la nature du bloc - suit le du nom du programme
- puis ses paramètres d'entrées entre parenthèses. Chaque paramètre est décrit par son nom suivit de son type
- enfin, le type de sortie de l'algorithme qu'on fait précéder d'une flèche.
Si on a besoin d'information supplémentaire pour qu'un lecteur puisse mieux comprendre le pseudo-code on peut ajouter des commentaires en les faisant commencer par un #. Ne mettez pas trop de commentaires, normalement le pseudo-code et le nom des variables doit suffire. On a ici juste décrit ce que fait l'algorithme avec ses paramètres d'entrées.
La description des types de paramètres est reprise du format python : https://docs.python.org/fr/3.13/library/typing.html
Terminons par un petit exercice :
Adaptez le pseudo code de l'algorithme recherche(T: [entier], x: entier) → booléen précédent pour créer l'algorithme :
nombre(T: [entier], x: entier) → entier
Cet algorithme rend le nombre de fois où x est présent dans T
corrigé
corrigé
algorithme nombre(T: [entier], x: entier) → entier:
nb := entier
nb ← 0
e := entier
pour chaque e de T:
si e == x:
nb ← nb + 1
rendre nb
Fonctions
Dans un programme, de nombreuses phases sont répétées à divers endroits du code (sommer ou multiplier des vecteurs 3D dans un moteur physique par exemple). Pour éviter de devoir replacer toutes ces instructions à chaque fois on définis des sous-programmes réutilisables à volonté, appelés fonctions.
Définition
Une fonction est un programme. Elle a ainsi :
- un nom
- des paramètres d'entrée
- une sortie
- des instructions, appelées corps de la fonction
Une fois définie, on peut l'appeler comme une instruction (sa sortie est affectée à la variable sortie dans l'exemple ci-après) :
sortie ← nom_fonction(entrée_1, ..., entrée_n)
Appels de fonctions
On peut définir des fonctions puis les utiliser ensuite comme dans tout langage de programmation. Considérons l'algorithme de recherche :
algorithme recherche(t: [entier],
x: entier
) → booléen:
e := entier
pour chaque e de t:
si e == x:
rendre Vrai
rendre Faux
On peut utiliser le pseudo code de nom recherche dans d'autres pseudo-code comme une fonction. Par exemple :
t := [entier]{longueur: 3}
t ← [1, 2, 6]
trouve := booléen
trouve ← recherche(t, 6)
affiche à l'écran trouve
Est un pseudo-code valide puisque recherche est bien défini et utilisé correctement (le type de ses paramètres est correct).
À retenir
- Les différentes exécutions de fonctions au sein d'un même algorithmes ne partagent pas leurs variables.
- Les paramètres sont des variables qui sont initialisées au début de l'exécution de la fonction.
- Seuls les objets peuvent être transmis.
Dans l'exemple précédent, la variable e définie dans la fonction recherche n'est pas visible depuis l'algorithme. En revanche, l'objet booléen de retour est lui transmis à l'algorithme via l'instruction rendre.
Pseudo-code avec fonctions
On peut aussi directement coder des fonctions dans un pseudo-code. Par exemple :
fonction recherche(t: [entier],
x: entier
) → booléen:
e := entier
pour chaque e de t:
si e == x:
rendre Vrai
rendre Faux
algorithme exorcisme(t: [entier]
) → chaîne:
si recherche(t, 666):
rendre "Aspergez votre ordinateur d'eau bénite. Vite !"
sinon:
rendre "Tout va bien, le tableau n'est pas possédé. Ouf."
Que va afficher l'exécution de l'algorithme suivant :
fonction affiche_double(e: entier) → ∅:
x := entier
x ← 2 * e
affiche x
algorithme calculatrice() → ∅:
x := entier
x ← demande un entier à l'utilisateur
affiche_double(x)
affiche x
corrigé
corrigé
L'entier x de la fonction est indépendant de l'entier x défini dans l'algorithme. L'exécution de l'algorithme affichera alors :
- le double de l'entier demandé à l'utilisateur : exécution de la ligne 5
- l'entier demandé à l'utilisateur : exécution de la ligne 13
Tableaux et fonctions
À retenir
Les objets sont communs à toutes les exécutions d'un même algorithmes. Ils peuvent passer d'une fonction à une autre via les variables.
Par exemple :
fonction modif(U: [entier]) → ∅:
U[2] ← U[0] + U[1]
algorithme tableau() → ∅:
T := [entier]{longueur: 3}
T[0] ← 1
T[1] ← 2
T[2] ← 0
affiche T[2]
modif(T)
affiche T[2]
Va afficher 0 puis 3. En effet :
- lors de l'exécution de la fonction
modifla variableUva être associée à l'objet tableau de l'algorithme : la variableTde l'algorithme est différente de la variableUde la fonction mais est associé au même objet. - on associe à la variable d'indice 2 de l'objet tableau un nouvel entier valant 3 : on a modifié le tableau défini dans l'algorithme.
Les tableaux sont les seuls objets défini en pseudo-code qui peuvent être modifiés, ce n'est pas le cas ni des 5 types basiques ni des chaînes.
À retenir
Vérifiez toujours très soigneusement vos algorithmes lorsque vous créez des tableau que vous passez en paramètres de fonctions. Ils peuvent être modifiés par cette fonction !
Type d'un algorithme ou d'une fonction
Lorsque l'on défini un algorithme ou un pseudo-code on explicite le type des objets en entrées et en sortie :
Définition
Une signature de fonction associe :
- son nom
- le type de chacun de ses paramètres
- le type de sortie
On le représente ainsi :
nom_de_l_algorithme(type_premier_paramètre, ..., type_dernier_paramètre) → type_de_sortie
Ou, lorsque le nom des paramètre peut-être utile on pourra les ajouter ainsi :
nom_de_l_algorithme(nom_premier_paramètre: type_premier_paramètre, ..., nom_premier_paramètre: type_dernier_paramètre) → type_de_sortie
Définition
Le type d'un algorithme est sa signature sans son nom.
Par exemple pour l'algorithme recherche :
- sa signature est
recherche([entier], entier) → booléen - son type est
([entier], entier) → booléen
Lorsque le nom des paramètres est significatif, on les ajoutera à la signature. On écrira par exemple division(dividende: réel, diviseur: réel) → réel plutôt que division(réel, réel) → réel qui ne nous permet pas directement d'utiliser la fonction.
Associer un type à une fonction permet lui permet d'être associée à un nom comme tout autre objet. Par exemple, en supposant que la fonction recherche soit définie :
f := ([entier], entier) → booléen
f ← recherche
affiche f([1, 2, 6], 6)
Ne confondez pas nom qui est l'algorithme et nom(a, b) qui est le résultat de son exécution avec les paramètres a et b.
Récursivité
Le fait que les variables et les noms définies dans les fonctions restent dans le cadre de la fonction actuellement exécuté nous donnent accès à la récursivité : Il suffit que notre pseudo-code s'appelle lui-même comme une fonction.
Attention aux conditions d'arrêts pour garantir qu'une fonction ne s'appelle pas indéfiniment.
Par exemple l'algorithme suivant est une implémentation récursive de l'algorithme recherche :
algorithme recherche(T: [entier],
x: entier # entier recherché dans t
) → booléen: # Vrai si x est dans t
si T.longueur == 0:
rendre Faux
sinon si T[0] == x:
rendre Vrai
sinon:
rendre recherche(T[1:], x)