Activité : Réaliser des fonctions (1)

Consigne

Prendre connaissance de l’activité et lire la capsule de théorie avant de commencer le travail de mise en route et de réaliser les tâches.

L’objectif est d’apprendre la syntaxe de la définition d’une fonction et d’exercer l’utilisation des instructions de base de Java (affectation et structures de contrôle), n’utilisez pas le copier-coller, tapez votre code.

Le code des tests unitaires doit être utilisé tel quel et ne doit pas être modifié. Le travail est individuel. Vous pouvez communiquer, mais en respectant le code d’honneur.

Mise en situation

Dans le cadre d’un projet, on vous demande de réaliser et de tester un certain nombre de fonctions. Vous avez reçu la spécification du module à réaliser, mais vous n’avez pas accès au code du projet. Vous devez donc fournir le code des ces fonctions sous la forme d’un module qui puisse être intégré au projet.

Résultat attendu

  • Un projet Maven avec une classe principale App et une classe nommée FuncLib qui contient les fonctions que vous aurez réalisées.

  • Tous les tests unitaires doivent passer avec succès.

Objectifs

À la fin de ce travail, vous devez :

  1. Savoir ce qu’est un sous‑programme.
  2. Savoir ce qui distingue une fonction d’une procédure.
  3. Savoir ce qu’est la signature de la fonction.
  4. Savoir ce qu’est le corps de la fonction.
  5. Connaître par cœur la syntaxe de la définition d’une fonction en Java.
  6. Être capable de réaliser une fonction et de l’utiliser.

Ressources

Logiciel :

  • Maven
  • Visual Studio Code

Documents :

Fragments de code (snippets):

Ces fragments de code vous sont fournis pour vous aider à réaliser vos programmes. Prenez la peine de taper le code plutôt que de faire du copier-coller, de façon à vous familiariser avec la syntaxe et ainsi, l’apprendre.

Le fragment ci-dessous est le squelette de la définition d’une classe. Une classe doit se trouver dans un fichier .java dont le nom est strictement identique à celui de la classe et ce fichier doit se trouver dans une structure de répertoire correspondant exactement au package.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// Espace de noms (package) de la classe
package ch.epai.ict.m404.projet; // Remplacer projet par le nom
                                 // de votre projet


// Définition de la classe MaClasse.
public class MaClass { // Remplacer MaClasse par le nom de votre
                       // classe en UpperCamelCase.

    // Si nécessaire, insérer ici les constantes. Par exemple:
    public static final int MA_CONSTANTE = 1; // Remplacer MA_CONSTANTE par  
                                               // le nom de votre constante  
                                               // en UPPER_SNAKE_CASE, int 
                                               // par le type adéquat et
                                               // 1 par la valeur souhaitée.

    // Une procédure est une séquence d’instruction qui ne 
    // renvoie pas de valeur et qui doit avoir un effet.
    // 
    // Le nom d'une procédure s'écrit en lowerCamelCase
    //
    // Adapter le nom, le type et le nombre des paramètres.
    public static void procedureName(int param1, int param2) {

        // Insérer ici les déclarations des variables
        // locales et les instructions de la procédure.
        //
        // Les noms des variables locales s'écrivent
        // en lowerCamelCase.
    }


    // Une fonction est une séquence d’instruction qui renvoie 
    // une valeur et qui ne doit pas avoir d’effet de bord.
    // 
    // Le nom d'une fonction s'écrit en lowerCamelCase
    //
    // Adapter le nom, le type et le nombre des paramètres 
    // Adapter le type de la valeur renvoyée
    public static int functionName(int param1, int param2) {

        // Insérer ici les déclarations des variables
        // locales et les instructions de la fonction.
        //
        // Les noms des variables locales s'écrivent
        // en lowerCamelCase.
        
        // renvoyer une valeur
        return 0; 
    }
}

Mise en route

Lancez votre machine de développement avec Vagrant et connectez Visual Studio Code (VSCode) à cette machine. Ouvrez le terminal intégré de VSCode, rendez-vous dans le répertoire de vos projets et créez un nouveau projet Maven avec la commande :

1
mvn archetype:generate -DarchetypeArtifactId="archetype-quickstart" -DarchetypeGroupId="ch.epai.ict.maven.archetype" -DarchetypeCatalog=internal -DinteractiveMode=false -DgroupId="ch.epai.ict.m404.activity2" -DartifactId="activity2"

Déplacez-vous dans le répertoire activity2 (avec la commande cd) et lancer la commande code . pour ouvrir ce répertoire dans VSCode.

Avec l’explorateur de fichier de VSCode (la fenêtre de gauche), rendez-vous dans le répertoire src/main/java/ch/epai/ict/m404/activity2. Dans ce répertoire, créez un fichier FuncLib.java et définissez la classe FuncLib en vous aidant des fragments de code fournis.

Maintenant que tout est prêt, on peut commencer. Pour le premier point, il s’agit de réaliser une fonction qui renvoie la plus petite de trois valeurs. En lisant la spécification, on apprend que la fonction doit avoir le nom minOfThree, qu’elle doit avoir trois paramètres de type double et renvoyer un double. On peut donc déjà écrire la signature de la fonction :

1
public static double minOfThree(double val1, double val2, double val3)

Il faut maintenant en réaliser une implémentation. Une stratégie pour résoudre ce problème pourrait être de faire l’hypothèse que val1 est la plus petite valeur, puis de tester cette hypothèse en la comparant d’abord à val2. Si l’hypothèse n’est pas vérifiée, on fait la nouvelle hypothèse que val2 est la plus petite valeur et on la teste en comparant cette valeur à val3. Si l’on traduit cela en Java, on obtient :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static double minOfThree(double val1, double val2, double val3) {
    // par hypothèse, la valeur  min est val1
    double min = val1;

    // test de l’hypothèse avec val2
    if (val2 < min) {
        // l’hypothèse n’est pas vérifiée.
        // nouvelle hypothèse : la valeur min est val2
        min = val2;
    }

    // test de l’hypothèse avec val3
    if (val3 < min) {
        // l’hypothèse n’est pas vérifiée.
        // la valeur min est val3
        min = val3;
    }

    // renvoie la valeur min
    return min;
}

Maintenant que la fonction est écrite, il reste à s’assurer qu’elle fonctionne. Pour cela, on peut réaliser un programme qui appelle la fonction et vérifier que le résultat correspond à ce que l’on attend.

Ouvrez le fichier App.java et copier la procédure main ci-après.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static void main( String[] args ) {
    double a = 1.0;
    double b = 2.0;
    double c = 3.0;
    
    // min en position 1 
    System.out.println(FuncLib.minOfThree(a, b, c));

    // min en position 2
    System.out.println(FuncLib.minOfThree(c, a, b));

    // min en position 3
    System.out.println(FuncLib.minOfThree(b, c, a));
}

Si vous exécutez ce code, vous devriez avoir trois fois la valeur 1.0 écrite dans la console de débogage. Cette façon de faire est acceptable et permet en outre d’utiliser le débogueur dans le cas où cela ne fonctionne pas. Toutefois, il est possible de faire mieux à l’aide de tests unitaires. Dans code ci-dessous, la classe FuncLibTest contient trois procédures qui correspondent à trois tests unitaires pour la fonction minOfThree. Chaque procédure permet de tester une combinaison de paramètre.

Avec l’explorateur de projet de VSCode, rendez-vous dans le répertoire src/test/java/ch/epai/ict/m404/activity2 et créez un nouveau fichier FuncLibTest.java et copier le code ci-après :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package ch.epai.ict.m404.activity2;

import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;

public class FuncLibTest {

    @Test
    public void testMinOfThreePos1() {
        double a = 1.0;
        double b = 2.0;
        double c = 3.0;

        double expectedValue = 1.0;
        double actualValue = FuncLib.minOfThree(a, b, c);
        double delta = 1e-3;

        // assertEquals est une assertion, c'est-à-dire une fonction
        // qui jette une exception si ce qu'elle affirme est faux (ici
        // la fonction affirme l'égalité de deux valeurs).
        // Dans le cas présent, assertEquals est utilisé pour tester
        // l'égaliter entre deux valeurs de type double. Comme il n'est
        // généralement pas possible d'avoir une égalité exacte entre
        // deux valeurs à virgule, on doit donner un paramètre
        // supplémentaier pour spécifier l'erreur admise (delta).
        // Ici l'erreur admise est 1e-3.

        assertEquals(expectedValue, actualValue, delta);
    }

    @Test
    public void testMinOfThreePos2() {
        double a = 1.0;
        double b = 2.0;
        double c = 3.0;

        double expectedValue = 1.0;
        double actualValue = FuncLib.minOfThree(c, a, b);
        double delta = 1e-3;

        assertEquals(expectedValue, actualValue, delta);
    }

    @Test
    public void testMinOfThreePos3() {
        double a = 1.0;
        double b = 2.0;
        double c = 3.0;

        double expectedValue = 1.0;
        double actualValue = FuncLib.minOfThree(b, c, a);
        double delta = 1e-3;

        assertEquals(expectedValue, actualValue, delta);
    }
}

Vous pouvez maintenant lancer l’exécution des tests en lançant la tâche test depuis VSCode (Shift-Ctrl-P puis Run Test Task) ou depuis la ligne de commande dans la fenêtre du terminal intégré (mvn test). Si tout se passe bien, vous devriez voir un message qui vous indique que deux tests ont été passés avec succès.

1
2
3
4
5
6
7
8
9
10
11
12
Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.004 s
[INFO] Finished at: 2018-05-25T10:48:16+02:00
[INFO] ------------------------------------------------------------------------

Terminal will be reused by tasks, press any key to close it.

Les tests unitaires sont exécutés à chaque fois que vous faites un « build » du projet avec la commande Run Build Task de VSCode, avec le raccourci clavier shift-ctrl-B ou avec la commande mvn verify dans une fenêtre de terminal.

Ajoutez les tests suivants dans la class FuncLibTest (copiez les procédures à la suite de celles qui s’y trouvent déjà) et effectuez les tâches décrites au point 8.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
@Test
public void testMaxOfThreePos1() {
    double a = 1.0;
    double b = 2.0;
    double c = 3.0;

    double expectedValue = 3.0;
    double actualValue = FuncLib.maxOfThree(a, b, c);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta);
}

@Test
public void testMaxOfThreePos2() {
    double a = 1.0;
    double b = 2.0;
    double c = 3.0;

    double expectedValue = 3.0;
    double actualValue = FuncLib.maxOfThree(c, a, b);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta);
}

@Test
public void testMaxOfThreePos3() {
    double a = 1.0;
    double b = 2.0;
    double c = 3.0;

    double expectedValue = 3.0;
    double actualValue = FuncLib.maxOfThree(b, c, a);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta);
}

@Test
public void testMeanOfThree() {
    double a = 1.0;
    double b = 2.0;
    double c = 3.0;

    double expectedValue = 2.0;
    double actualValue = FuncLib.meanOfThree(a, b, c);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta);

}

@Test
public void testMinOfNPos1() {

    int[] values = { 1, 2, 3 , 4 };

    int expectedValue = 1;
    int actualValue = FuncLib.minOfN(values);

    assertEquals(expectedValue, actualValue);
}

@Test
public void testMinOfNPosN() {

    int[] values = { 2, 3, 4, 1 };

    int expectedValue = 1;
    int actualValue = FuncLib.minOfN(values);

    assertEquals(expectedValue, actualValue);
}


@Test
public void testMaxOfNPos1() {

    int[] values = { 1, 2, 3 , 4 };

    int expectedValue = 4;
    int actualValue = FuncLib.maxOfN(values);

    assertEquals(expectedValue, actualValue);
}

@Test
public void testMaxOfNPosN() {

    int[] values = { 2, 3, 4, 1 };

    int expectedValue = 4;
    int actualValue = FuncLib.maxOfN(values);

    assertEquals(expectedValue, actualValue);
}

@Test
public void testMeanOfN1() {

    int[] values = { 1 };

    double expectedValue = 1;
    double actualValue = FuncLib.meanOfN(values);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta);
}

@Test
public void testMeanOfN2() {

    int[] values = { 1, 2, 3, 4 };

    double expectedValue = 2.5;
    double actualValue = FuncLib.meanOfN(values);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta);
}

@Test
public void testMeanOfN3() {

    int[] values = {};

    double expectedValue = 0;
    double actualValue = FuncLib.meanOfN(values);
    double delta = 1e-3;

    assertEquals(expectedValue, actualValue, delta, "Si le tableau est vide la fonction doit renvoyer 0");
}

Tâches

Dans la classe FuncLib, ajoutez les fonctions ci-après :

  1. Une fonction maxOfThree qui renvoie la plus grande de trois valeurs de type double.

  2. Une fonction meanOfThree qui renvoie la moyenne arithmétique de trois nombres de type double.

  3. Les fonctions minOfN, maxOfN, meanOfN qui renvoient, respectivement, la plus petite valeur, la plus grande valeur et la moyenne des valeurs d’un tableau d’entiers de taille quelconque. (voir si nécessaire l’activité « Tableaux de valeurs » du module 403)

Tâches optionnelles

Si toutes les tâches du point précédent ont été réalisées, vous pouvez encore ajouter la fonction suivante dans la classe FuncLib.

  1. Une fonction frequenciesOfN qui renvoie un tableau à deux dimensions contenant les fréquences des différentes valeurs d’un tableau d’entiers (voir les tests unitaires ci-après). Voici quelques pistes pour vous aider à réaliser cette fonction :
    • La procédure Arrays.sort(int[] a) de la bibliothèque standard de Java trie le tableau passé en paramètre.
    • La fonction int[][] Arrays.copyOf(int[][] original, int newLength) de la bibliothèque standard de Java permet de redimensionner la première dimension d’un tableau. La fonction renvoie un tableau de taille newLength qui contient une copie des newLength premières valeurs du tableau original passé en paramètre.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Test
public void testfrequenciesOfN2() {

    int[] values = { 1, 2, 2, 5, 7, 7, 7, 9 };

    int[][] expectedValue =
        {
            {1, 1}, // valeur, fréquence
            {2, 2},
            {5, 1},
            {7, 3},
            {9, 1}
        };

    int[][] actualValue = FuncLib.frequenciesOfN(values);
    Assert.assertNotNull("La fonction doit renvoyer un tableau.", actualValue);
    Assert.assertEquals("Le taille du tableau n'est pas correcte.", expectedValue.length, actualValue.length);
    for (int i = 0; i < expectedValue.length; i += 1) {
        Assert.assertArrayEquals("La fréquence n'est pas correcte", expectedValue[i], actualValue[i]);
    }
}

@Test
public void testModeOfN3() {

    int[] values = { 9, 2, 9, 3, 2, 9, 4, 2, 4, 2};

    int[][] expectedValue =
    {
        {2, 4}, // valeur, fréquence
        {3, 1},
        {4, 2},
        {9, 3},
    };

    int[][] actualValue = FuncLib.frequenciesOfN(values);
    Assert.assertNotNull("La fonction doit renvoyer un tableau.", actualValue);
    Assert.assertEquals("Le taille du tableau n'est pas correcte.", expectedValue.length, actualValue.length);
    for (int i = 0; i < expectedValue.length; i += 1) {
        Assert.assertArrayEquals("La fréquence n'est pas correcte", expectedValue[i], actualValue[i]);
    }    
}