Algorithmes et fonctions

Le pseudo-code permet d'écrire des algorithmes de façon claire et sans ambiguïté.

Algorithme

Définition

Un algorithme est une suite d'instructions de pseudo-code qui, à partir de paramètres d'entrée de type déterminé, rend une sortie de type déterminé. En pseudocode comme un bloc :

algorithme <nom>(<nom paramètre>: <type paramètre>, ...) → <type de sortie>
    # ... instructions

    rendre <objet>

Il est défini avec :

  • son nom,
  • ses paramètres (0, 1 ou plusieurs séparés par des virgules) et son type de retour
  • la dernière instruction d'un algorithme est l'instruction rendre <objet>

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: []int,
                     x: int      # entier recherché dans t
                    ) → bool:    # Vrai si x est dans t

    pour chaque e de T:
        si e == x:
            rendre Vrai
    rendre Faux

La définition du bloc algorithme (jusqu'aux :) est constituée :

  1. du mot-clé algorithme qui détermine la nature du bloc
  2. du nom de l'algorithme : recherche
  3. puis ses 2 paramètres d'entrée. Chaque paramètre est décrit par son nom suivi de son type.
  4. enfin, le type de sortie de l'algorithme qu'on fait précéder d'une flèche.

Si on a besoin d'informations supplémentaires 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 doivent suffire. On a ici juste décrit ce que fait l'algorithme avec ses paramètres d'entrée.

La description des types de paramètres est reprise du format python : https://docs.python.org/fr/3.13/library/typing.html

Tout algorithme se termine par un retour d'un objet (de même type que son type de sortie) avec l'instruction rendre. Cette instruction est la dernière qu'exécutera l'algorithme ainsi l'algorithme suivant rendra toujours 42 :

algorithme réponse() → int: 

    rendre 42
    rendre 0    # ne sera jamais exécuté

Ne confondez pas le retour d'un algorithme et un affichage à l'écran. L'algorithme suivant rendra l'entier 0 :

algorithme réponse() → int: 

    affiche 42
    rendre 0

L'affichage à l'écran est une information donnée à l'utilisateur, l'algorithme s'en fiche et fonctionne tout à fait sans.

À retenir

Pour distinguer le retour de fonction d'un affichage supprimez tous les affichages de votre pseudo-code et il doit continuer de fonctionner.

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éfinit des sous-programmes réutilisables à volonté, appelés fonctions.

Définition

Une fonction est un algorithme. 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>(<paramètre 1>, ..., <paramètre 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: []int,
                     x: int
                    ) → bool:

    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-codes comme une fonction. Par exemple :


ref T []int ← [1, 2, 6]
ref 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 algorithme 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, tout se passe comme si on disposait de deux ordinateurs, un qui exécute le pseudo-code, l'autre la fonction recherche :

  1. le passage de l'ordinateur effectuant le pseudo-code à l'ordinateur exécutant l'algorithme recherche se fait en affectant les paramètres de la fonction recherche (ce sont des variables) à des objets manipulés par le pseudo-code : ici un tableau valant [1, 2, 3]
  2. le passage de l'ordinateur effectuant l'algorithme recherche à l'ordinateur exécutant le pseudo-code se fait par le retour de la fonction

Dans l'exemple précédent, la variable e définie dans l'algorithme recherche n'est pas visible depuis le pseudo-code. En revanche, l'objet booléen de retour est lui transmis au pseudo-code via l'instruction rendre.

Pseudo-code avec fonctions

On peut aussi directement coder des fonctions dans un pseudo-code. Par exemple :

fonction recherche(T: []int, x: int) → bool:

    pour chaque e de T:
        si e == x:
            rendre Vrai
    rendre Faux

algorithme exorcisme(T: []int) → str:
    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: int) → ∅:

    ref x ← 2 * e
    affiche x

algorithme calculatrice() → ∅:

    ref x int

    x ← demande un entier à l'utilisateur
    affiche_double(x)
    affiche x

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 :

  1. le double de l'entier demandé à l'utilisateur : exécution de la ligne 5
  2. 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 algorithme. Ils peuvent passer d'une fonction à une autre via les variables.

Par exemple :

fonction modif(U: []int) → ∅:

    U[2] ← U[0] + U[1]

algorithme tableau() → ∅:

    ref T ← [3]int

    T[0] ← 1
    T[1] ← 2
    T[2] ← 0
    affiche T[2]
    modif(T)
    affiche T[2]

Va afficher 0 puis 3. En effet :

Les tableaux sont les seuls objets définis 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 tableaux que vous passez en paramètres de fonctions. Ils peuvent être modifiés par cette fonction !

Signature d'un algorithme ou d'une fonction

Lorsque l'on définit un algorithme ou un pseudo-code on explicite le type des objets en entrée 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ètres peut être utile on pourra les ajouter ainsi :

<nom de l'algorithme>(<nom premier paramètre>: <type premier paramètre>, ..., <nom dernier 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 :

Associer un type à une fonction lui permet d'être associée à un nom comme tout autre objet. Par exemple, en supposant que la fonction recherche soit définie :

ref f ([]int, int) → bool

f ← recherche
affiche f([1, 2, 6], 6)

Ne confondez pas nom_algorithme qui est l'algorithme et nom_algorithme(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éfinis dans les fonctions restent dans le cadre de la fonction actuellement exécutée nous donne accès à la récursivité : il suffit que notre pseudo-code s'appelle lui-même comme une fonction.

Attention aux conditions d'arrêt 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: []int,
                     x: int     # entier recherché dans t
                    ) → bool:   # 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)