abcdAlgos

abcdAlgosCollectif d'auteurs [x]

Un tutoriel pour se familiariser avec les fonctions.

Objectif

Le but de ce tutoriel est de se familiariser avec les fonctions. Il faut déja savoir utiliser l'interface, se servir de variables et avoir découvert l'instruction conditionnelle.

Introduction

  1. Décortiquer un exemple de fonction.

    Voici un exemple de fonction qui se nomme «valeur absolue1» et qui se définit ainsi:
    • Si la variable x a une valeur est positive alors on retourne cette valeur positive.
    • Si la variable x a une valeur est négative alors on retourne l'opposé de cette valeur (noté -x), donc une valeur encore positive.

    // Définition de la fonction valeur absolue

    int abs(int x) {
        if (x > 0) {
            return x;
        } else {
            return -x;
        }
    }
    Regardons plus en détail cette fonction :
    • La ligne de commentaire "Définition de la fonction valeur absolue" explique à tous ceux qui vont utiliser le code, ce que fait cette fonction : c'est bien utile !
    • La ligne int abs(int x) nous dit, de gauche à droite, que :
      • la fonction renvoie, en sortie, un résultat qui est un entier, donc un int ;
      • le nom de la fonction est abs ;
      • la fonction reçoit, en entrée, un argument x qui est un entier ;
      Cette ligne définit donc la fonction, c'est son interface ou sa signature. Nous avons besoin de connaître cette ligne pour savoir comment appeler cette fonction.
    • Les 4 lignes suivantes forment le corps de la fonction : on y lit que si x est plus grand que 0, le résultat est x, sinon le résultat est -x. En effet :
      • l'instruction return x; signifie renvoyer le résulat : le calcul est fini et la fonction retourne la valeur calculée.
      • On note aussi les accolades { } qui permettent de délimiter le corps de la fonction : ce sont comme des parenthèses qui encapsulent les instructions.
  2. Faire en sorte de réutiliser des portions de code.

    Prenons un exemple. Voici un bout de code, sûrement fort utile, qui calcule la plus grande des valeurs absolues1 de deux variables x et y. Si nous notons abs(x) la valeur absolue1 de x, et max(x, y) la plus grande valeur entre x et y. Eh bien nous cherchons à calculer max(abs(x), abs(y)), c'est à dire le maximum des deux valeurs absolues. Voici ... le code (jeter un coup d'oeil et passer à la suite !):
    void main() {
        int x = 12;
        int y = -14;
        int z;

    // Calcul selon que x ou y soit positif ou négatif, de la plus grande valeur positive entre x et -x, y et -y

        if (x > 0) {
            if (y > 0) {
                if (x > y) {
                    z = x;
                } else {
                    z = y;
                }
            } else {
                if (x > -y) {
                    z = x;
                } else {
                    z = -y;
                }
            }
        } else {
            if (y > 0) {
                if (-x > y) {
                    z = -x;
                } else {
                    z = y;
                }
            } else {
                if (-x > -y) {
                    z = -x;
                } else {
                    z = -y;
                }
            }
        }
        println("z = " + z);
    }
    Mmmm ... et bien oui, il est un peu long, un peu lourd et s'il faut le recopier chaque fois que nous avons besoin de calculer la plus petite des valeurs absolues, celà va vite devenir ingérable de fait. En plus, on mélange calcul de la valeur absolue et calcul du maximum et tout cela semble redondant et bien inutilement complexe. Nous avons une façon bien plus élégante de définir le même calcul en utilisant des fonctions:

    // Définition de la fonction maximum de deux valeurs

    int max(int x, int y) {
        if (x > y) {
            return x;
        } else {
            return y;
        }
    }

    // Définition de la fonction valeur absolue

    int abs(int x) {
        if (x > 0) {
            return x;
        } else {
            return -x;
        }
    }
    void main() {
        int x = 12;
        int y = -14;
        int z = max(abs(x), abs(y));
        println("z = " + z);
    }
    Ouf ! Ici tout a changé :
    • Le code est beaucoup plus court et plus lisible, beaucoup plus facile à vérifier aussi.
    • Le programme principal void main ne contient que ce que nous voulons faire, et la façon de le faire est déléguée aux fonctions.
    • Chaque fonction est définie de manière modulaire et pourra être facilement réutilisée : nous créons ici nos propres instructions, en plus de celles disponibles dans le langage2.
    Regardons plus en détail la fonction max :
    • La ligne de commentaire "Définition de la fonction maximum de deux valeurs" explique à tous ceux qui vont utiliser le code, ce que fait cette fonction : c'est indispensable !
    • La ligne int max(int x, int y) nous dit, de gauche à droite, que :
      • la fonction renvoie, en sortie, un résultat qui est un entier, donc un int ;
      • le nom de la fonction est max ;
      • la fonction reçoit, en entrée, deux arguments qui sont les entiers x et y;
      Cette ligne définit donc la fonction, c'est son interface ou sa signature. Nous avons besoin de connaître cette ligne pour savoir comment appeler cette fonction.
    • Les 4 lignes suivantes forment le corps de la fonction : on y lit que si x est plus grand que y, le résultat est x, sinon le résultat est y. En effet :
      • l'instruction return x; ou return y; signifie renvoyer le résulat : le calcul est fini et la fonction retourne la valeur calculée.
      • On note aussi les accolades { } qui permettent de délimiter le corps de la fonction : ce sont comme des parenthèses qui encapsulent les instructions.
    • Dans la partie principale du programme à la ligne int z = max(abs(x), abs(y)); on utilise la fonction, de la manière suivante:
      • Les valeurs d'entrée sont abs(x) (la valeur absolue de x elle même calculée en appelant la fonction abs) et abs(y).
      • Le calcul du maximum s'effectue à l'intérieur de la fonction max et le résultat est renvoyé dans z, puisque le = affecte la valeur à z.
    Nous avons ainsi structuré notre code.
  3. A propos des fonctions que nous connaissons déja.

    Nous connaissons déjà :
    • La fonction readString() ; qui renvoie une chaine de caractère entrée au clavier par l'utilisateur :
      • Cette fonction n'a pas d'argument en entrée (donc il n'y a rien entre les parenthèses), puisque nous n'avons besoin de lui passer aucun paramètre, tandis que l'entrée (par l'utilisateur) est externe au programme.
      • Cette fonction renvoie une valeur : la chaine lue sur le clavier.
    • La fonction println(); qui imprime une chaine de caractère en sortie pour l'utilisateur :
      • Cette fonction a un argument en entrée, la chaine de caratères, notée ici s, qui doit être imprimée.
      • Cette fonction ne renvoie aucun résultat en sortie, puisque la sortie est externe au programme (en affichant à l'écran). C'est le mot void, vide, qui signifie "rien" pas de résultat. Voilà le sens de ce mot du langage : l'instruction est "imprime et c'est tout".
    • La fonction void main(); ... c'est nous qui la définissons à chaque fois. Elle n'a (dans notre contexte) ni entrée ni sortie (donc rien entre les parenthèses, et le mot void pour signifier l'absence de résultat). Elle correspond juste au lancement du programme, à la fonction par laquelle le programme doit commencer.
  4. Les fonctions peuvent elle s'appeler elle-même ?.

    Imaginons une fonction A qui appelle, selon ses entrées, une fonction B qui appelle elle-même la fonction A ou tout autre combinaison telle que, finalement et selon ses entrées, la fonction A se rappelle elle-même. On dit qu'elle est récursive : cela peut donner des boucles infinies (donc des bugs !), ou des calculs très complexes et très intéressants ... et c'est plus compliqué que ce que nous pouvons découvrir ici, alors retenons juste deux choses :
    • Il faut éviter de définir des fonctions récursives, sauf dans les cas indispensables.
    • Il faut être prudent si c'est le cas, et bien comprendre la notion de boucle avant.

Travail proposé

  1. A nous de jouer.

    • Recopier les deux portions de code dans deux fichiers différents et vérifier si les deux donnent le bon résultat.
    • A partir du 2ème exemple de code, ajouter la fonction "min" (qui renvoie la plus petite des valeurs x ou y), pour calculer la plus petite des valeurs absolues, et non la plus grande. Vérifier le résultat.
    • A partir du 2ème exemple de code, puisque la valeur absolue1 de x est aussi la plus grande entre x et -x, reprogrammer la fonction valeur absolue en une ligne, sans utiliser de if, mais en utilisant la fonction max.
  2. Programmer d'autres fonctions.

    Programmer les fonctions suivantes et faire un petit main pour un programme de test :
    • Programmer la fonction "division sans bêtise" double div(double x, double y), qui renvoie
      • x divisé par y (cela se note comme sur les calculettes x / y), si y n'est pas zéro,
      • imprime un message d'erreur, puis renvoie la valeur Double.NaN, si y est nul (y == 0) .
      (la valeur Double.NaN qui se lie "Not-a-Number" signifie que le résultat n'est pas un nombre, mais est indéfini.
    • Programmer la fonction "ou exclusif" boolean xor(boolean x, boolean y), découverte avec l'instruction conditionnelle, qui renvoie true (vrai) si soit x, soit y est true mais pas les deux à la fois.
    • Programmer la fonction "maximum de trois entiers" int max(int x, int y, int z), soit en utilisant la fonction int max(int x, int y), soit des if, comme vous voulez.
    • Programmer une fonction "info ou intox" void infox(String question, boolean reponse); qui :
      • pose une question Vrai/Faux à l'utilisateur (voir des exemples ci dessous),
      • lit sa réponse avec un readBoolean();,
      • le félicite s'il a raison et lui dit gentiment s'il s'est trompé.
      L'utiliser par exemple avec le main suivant.
      void main() {
          infox("Il y a environ 100 millions d'ordinateurs dans le monde", false);

      //  ... il y en plus de 1 milliard!

          infox("Ada Byron-Lovelace était, dès le XIXème siècle, la 1ère femme informaticienne", true);

      //  ... oui elle fut la première à formaliser la notion de programme que nous apprenons ici.

          infox("Toutes les fonctions mathématiques peuvent être calculées par un ordinateur", false);

      //  ... il y a beaucoup de fonctions dont le temps de calcul est ... infini ! (donc impossible à calculer)

          infox("Le cours de la 2nd guerre mondiale a changé plus vite grâce à ... un calcul informatique", true);

      //  ... Alan-Turing a pu "craquer" le code de la machine Enigma qui codait les messages nazis grace à un calcul mécanique.

      }
      ... non sans vous amuser à ajouter vos propres questions !

Remarques

  1. A propos la valeur absolue.

    Si x est négatif sa valeur, par exemple -12 ou -14, la valeur absolue est le nombre positif correspondant sans le signe "-", autrement dit l'opposé de x, donc 12 ou 14 pour nos exemples. Si x est positif sa valeur absolue est le nombre lui même, autrement dit x. On note |x| la valeur absolue de x. Comme un nombre positif est toujours plus grand qu'un nombre négatif, c'est aussi la valeur la plus grande entre x et -x.
  2. Les fonctions «abs» et «max» en Java.

    Bien entendu les fonctions valeur-absolue, maximum-de-deux-valeurs existent déja dans tous les langages usuels dont Java ! Nous les avons "caché" en Javascool pour vous apprendre comment les programmer.