Un Bug dans la méthode

../../_images/shadock.jpg

Les objectifs de ce TP sont :

  • Écrire des méthodes java implémentant des traitements simples.

Les fonctions ou méthodes

Les fonctions permettent de décomposer un programme en plusieurs parties. Le comportement d’une fonction est paramétré par un ensemble de variables : les paramètres de la fonction. À la fin de son exécution, une fonction retourne généralement un résultat : sa valeur de retour. Les fonctions permettent de faciliter l’écriture et la (re-)lecture des programmes. Elles permettent également de réutiliser facilement des morceaux de programmes.

Une fonction est définie par :

  • un nom ;

  • une liste des paramètres : c’est une liste de variables typées dont la valeur sera renseignée lors de l’utilisation de la fonction ;

  • un type de retour : c’est le type de la valeur retournée par la fonction à la fin de son exécution ;

  • le corps de la fonction : c’est le code Java décrivant ce que fait la fonction.

Note

En Java, les fonctions sont aussi appelées des méthodes. Dans le cadre de ce cours d’introduction, nous utiliseront essentiellement des méthodes dites de classe (mot clef static).

Définition d’une fonction

En Java, la définition d’une fonction a la forme suivante :

static type_de_retour nom_de_la_fonction(liste des paramètres) {
  // corps de la fonction
}

Les fonctions doivent être définies en dehors de toute autre fonction (il n’est pas possible de définir une fonction dans une autre).

Type de retour

Le type de retour de la fonction est le type de la valeur retournée par la fonction. Tous les types usuels (int, float, boolean, etc.) sont possibles. Dans le cas particulier où la fonction ne retourne aucune valeur, on utilisera le mot clef void à la place du type de retour.

Nom de la fonction

Le choix des noms de fonctions suivent les mêmes règles que les noms de variables : le nom doit commencer par une lettre ou un tiret bas (_), et ensuite être composé d’une suite de lettres, de chiffres ou de tirets bas. Il est très fortement conseillé de choisir des noms significatifs, c.-à-d. donnant une indication sur ce que fait la fonction.

Liste des paramètres

Les paramètres sont des variables dont la valeur est renseignée avant d’utiliser la fonction. Ces variables sont données dans la liste des paramètres. Chaque paramètre est défini par son type, suivi de son nom. Lorsqu’une fonction prend plusieurs paramètres, leurs définitions sont séparées par des virgules. Lorsqu’une fonction ne prend aucun paramètre, le nom de la fonction est simplement suivi par une paire de parenthèses vides ().

Note

Les variables définies par les paramètres n’existent que pendant l’exécution de la fonction. Elles sont créées au début de l’exécution de la fonction et disparaissent lorsque la fonction retourne.

Corps de la fonction

Le corps de la fonction est ensuite décrit dans un bloc délimité par des accolades {..}. Le corps de la fonction consiste en une suite d’instructions Java. Comme dans tout bloc d’instructions, il est possible de déclarer des variables qui n’existeront que le temps de l’exécution du bloc.

L’instruction return provoque l’arrêt immédiat de l’exécution de la fonction. Ce mot clef doit être suivi d’une expression dont l’évaluation donnera la valeur retournée par la fonction. Le type de cette expression doit bien évidemment correspondre au type de retour déclaré pour la fonction. Si la fonction ne retourne rien (type de retour void), le mot clef return n’est suivi de rien (hormis le point-virgule terminant l’instruction).

Une instruction return est implicite à la fin de chaque fonction. Il est donc possible de l’omettre pour des fonctions ne retournant rien.

Appel de fonction

Lorsqu’une fonction est utilisée, on parle d’appel de fonction. Pour appeler une fonction, il suffit d’écrire son nom suivi d’une liste d’expressions entre parenthèses, séparées par des virgules. Ces expressions donneront les valeurs que prendront les paramètres de la fonction pendant son exécution. Une fonction retournant une valeur peut être utilisée au sein d’une expression.

En Java, les paramètres sont toujours passés par copie. Cela signifie que la fonction reçoit une copie des valeurs passées en paramètre.

Quelques conseils

Voici une liste de conseils afin d’utiliser correctement les fonctions :

  • Le nom de la fonction doit être choisi soigneusement : il doit refléter ce que fait la fonction.

  • De même que le nom de la fonction, c’est une bonne idée de donner des noms significatifs aux paramètres des fonctions.

  • Il est conseillé d’essayer de ne mettre qu’une seule instruction return dans une fonction. Il faut parfois retravailler l’algorithme pour y arriver. Ceci n’est cependant pas une règle absolue : l’objectif est d’écrire du code qui soit clair et facile à lire et à comprendre.

  • Le corps d’une fonction ne doit bien souvent pas dépasser quelques (2 ou 3) dizaines de lignes. Si une fonction devient trop longue, c’est peut-être qu’il faut la décomposer en plusieurs fonctions.

  • On veillera à indiquer avant chaque fonction, à l’aide d’un commentaire, le rôle de la fonction, une description des

différents paramètres ainsi que de la valeur retournée.

Défi n°1 : L’artiste Bug

État initial

  • Un Bug dans une grille de 10x10 cases, avec pour position initiale la case (0,0).

Résultat attendu

  • Le Bug a dessiné des fleurs centrées sur les positions (2,6) (7,6) et (5,4).

../../_images/init1.png ../../_images/result1.png

À faire

  1. Récupérer l'archive du programme et l’enregistrer dans le dossier du TP.

  2. Se placer dans le dossier puis décompresser l’archive :

    file-roller --extract-to=.  TP_BugMethodes.tar.gz
    

    Le programme comprend les classes et fichiers devenus habituels. Se reporter à l’énoncé du premier TP pour les détails et la référence de l’API (en cliquant ici).

  3. Le programme, tel qu’il est fourni, produit des erreurs à la compilation car la définition de la méthode à écrire n’est pas complète.

  4. Déterminer le type de retour approprié pour la méthode dessineLosange() de la classe Bug.

  5. Compléter la définition de la méthode et valider son fonctionnement.

  6. Modifier la méthode enRoute() de sorte à dessiner plus de fleurs (à d’autres endroits).

Note

  • Penser à positionner la variable readMaze à false dans le fichier Main.java.

Correction

Bug.java

Défi n°2 : Le Bug chasseur de serpent

État initial

  • Un Bug dans une grille de 10x10 cases, placé à l’extrêmité d’un serpent imaginaire formé par les cases de couleur verte.

Résultat attendu

  • Le Bug a suivi le corps du serpent jusqu’à la tête et mangé la tête.

../../_images/init2.png ../../_images/result2.png

À faire

  1. Récupérer le code de la classe Bug et l’enregistrer à la place de la précédente version. Le programme, tel qu’il est fourni, se compile mais ne réalise rien.

  2. Concevoir et implémenter la méthode isFaceSnake() qui retourne vrai si le bug fait face à une case verte et faux sinon. Elle doit également retourner faux si le bug fait face à un mur. Enfin, à la fin de l’exécution de cette méthode, ni le bug ni le monde des bugs ne doit avoir été modifié.

Les bugs apprécient aussi bien les limaces et les lézards que les serpents ; mais ces délicieuses proies ne sont pas toutes de la même couleur et la méthode isFaceSnake() ne convient donc pas pour dévorer une belle grosse limace marron ou un lézard orange.

À faire

  1. Modifier la méthode isFaceSnake() de sorte qu’elle permettre de suivre une piste dont on lui spécifie la couleur. Pour tester le fonctionnement de cette nouvelle version de la méthode, changer la couleur de la proie dans le fichier Main.java (COULEUR_PP) et positionner les cases colorées :

    final int[][] casesColorees = {
          {6,0},{6,1},{6,2},
          {5,2},{5,4},{5,5},{5,6},
          {4,2},{4,3},{4,4},{4,6},
          {3,6},{3,7},{3,8},
          {2,8},
          {1,8}
    };
    

Correction

Bug.java


Défi n°3 : Le binary Bug

État initial

  • Un Bug dans une grille de 10x10 cases, placé à l’extrêmité d’un ruban imaginaire formé par des cases de couleur verte.

  • Les cases contenant un biscuit sont considérées comme représentant un 1 logique, les cases vides représentant un 0.

Résultat attendu

  • Le Bug a suivi le ruban, lu la valeur binaire encodée par les biscuits et transcodé cette valeur en décimal.

../../_images/init3.png ../../_images/result3.png

À faire

  1. Récupérer le code de la classe Main et l’enregistrer à la place de la précédente version.

  2. Récupérer les positions des biscuits et l’enregistrer.

  3. Compléter la méthode enRoute() de la classe Bug pour qu’elle fasse en sorte que le bug suive le ruban (précédemment serpent), repère les biscuits qui symbolisent des états logiques 1 et construise le mot binaire qu’ils définissent. En fin de Ruban, le bug émettra un message contenant le mot binaire qu’il a lu. On considèrera, pour cette question, que le bug parcourt le ruban en commençant par le bit de poids fort. Cela pourra ressembler à :

    Bug 0 dit : J'ai lu 1100000010011101
    

    Indication

    La méthode concat() de la classe String pourra s’avérer utile (mais pas indispensable).

  4. Concevoir une méthode binaryString2IntNatural() qui permette maintenant au bug de nous réveler la représentation décimale du mot binaire qu’il a lu précédemment. Elle accepte en paramètre une chaîne de caractères représentant le mot binaire à transcoder et calcule la valeur correspondante avant de la retourner.

    Indication

    Les méthodes length() et charAt() de la classe String peuvent rendre ici de grands services.

Cela étant fait, il serait dommage de se priver des nombres entiers négatifs, alors on décide que les mots binaires encodés sur le ruban sont représentés en complément à 2 (C2). Pour rappel la représentation en C2 permet de représenter sous forme binaire les nombres entiers positifs et négatifs et le passage entre la représentation binaire naturelle et le C2 se fait simplement avec le procédé suivant :

SI (bit de poids fort = 0) ALORS il s'agit d'un nombre positif à lire en binaire naturel
SINON
  il s'agit d'un nombre négatif := - (valeur du complément à 1 +1)
FIN_SI

Indication

Le complément à 1 d’un nombre est la représentation binaire obtenue en remplaçant chaque 0 par un 1 et inversement.

  1. Concevoir une méthode binaryString2IntC2() qui retourne la valeur représentée par le mot binaire passé en paramètre, supposé écrit en complément à 2.

Il s’avère aussi que le ruban peut présenter une longueur différente de 16. On choisit alors d’appliquer la politique de lecture suivante :

  • si le ruban comporte moins de 16 cases, on compléte la représentation par des 0.

  • si le ruban comporte plus de 16 cases, on arrête la lecture dès que l’on a lu 16 digits.

Indication

Dans l’exemple de l’illustration, la valeur représentée est -16227.

Cependant, cela n’a de sens que si la lecture s’effectue en commençant par le bit de poids faible, de sorte que les éventuels 0 ajoutés ne soient pas significatifs. Enfin, on veut rendre ce programme capable de décoder des nombres sous des formats différents de 16 bits.

  1. Modifier le code de la classe Bug pour refléter les modifications décrites ci-desus. On limitera les tests à des formats inférieurs à 16 bits pour des raisons de taille de grille.

    Indication

    • Il est nécessaire de modifier la méthode enRoute() et vraisemblablement la méthode binaryString2IntC2() de sorte à ce que le format devienne un paramètre de la méthode.

    • Un ruban plus petit pourrait être défini par

      final int[][] casesColorees = {
                      {5,2},{5,4},{5,5},{5,6},
                      {4,2},{4,3},{4,4},{4,6},
                      {3,6},{3,7},{3,8},
                      {2,8},
                      {1,8}
              };
      

      Il faut alors penser à supprimer les 2 premières lignes du fichier de positionement des biscuits binaryData.txt.

    • Un ruban plus grand pourrait être défini par

      final int[][] casesColorees = {
                      {9,0},{8,0},{7,0},
                      {6,0},{6,1},{6,2},
                      {5,2},{5,4},{5,5},{5,6},
                      {4,2},{4,3},{4,4},{4,6},
                      {3,6},{3,7},{3,8},
                      {2,8},
                      {1,8}
              };
      

Correction

Bug.java. ATTENTION, la méthode isFaceSnake() est remplacée par hasNextSnake(), conçue différemment, dans cette version, pour varier.