Activité : Réaliser des fonctions (1)

Consigne

Prendre connaissance de l’activité et lire la capsule de théorie avant de commencer la 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), ne faites donc pas de copier-coller, mais 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.

Situation

On vous demande de réaliser et de tester un certain nombre de fonctions dans le cadre d’un projet. On vous a donné la spécification du module à réaliser, mais vous n’avez pas accès au code du projet. Vous devez donc fournir le code du module pour qu’il 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 qui vous sont demandées.

Objectifs

À la fin de ce travail, vous devez :

  1. Connaître par cœur la syntaxe de la définition d’une fonction
  2. Ê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

Rendez-vous dans le répertoire de vos projets et créez un nouveau projet Maven avec la commande :

1
mvn archetype:generate -DgroupId=ch.epai.ict.m404.activity2 -DartifactId=activity2 -DarchetypeArtifactId=archetype-quickstart-jdk8 -DarchetypeGroupId=com.github.ngeor -DinteractiveMode=false

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

Avec l’explorateur de fichier de VSC (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 VSC, 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
57
58
59
package ch.epai.ict.m404.activity2;

import org.junit.*; 
import org.junit.Assert;

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 epsilon = 0;

        // 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 (epsilon).
        // Ici l'erreur admise est 0 car la fonction ne fait pas 
        // de calcul mais renvoie directement la valeur passée
        // en paramètre.

        Assert.assertEquals(expectedValue, actualValue, epsilon);
    }

    @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 = 0;

        Assert.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 = 0;

        Assert.assertEquals(expectedValue, actualValue, delta);
    }

}

Vous pouvez maintenant lancer l’exécution des tests en lançant la tâche test depuis VSC (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 VSC, 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 = 0;

    Assert.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 = 0;

    Assert.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 = 0;

    Assert.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 = 0;

    Assert.assertEquals(expectedValue, actualValue, delta);

}

@Test
public void testMinOfNPos1() {

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

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

    Assert.assertEquals(expectedValue, actualValue);
}

@Test
public void testMinOfNPosN() {

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

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

    Assert.assertEquals(expectedValue, actualValue);
}


@Test
public void testMaxOfNPos1() {

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

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

    Assert.assertEquals(expectedValue, actualValue);
}

@Test
public void testMaxOfNPosN() {

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

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

    Assert.assertEquals(expectedValue, actualValue);
}

@Test
public void testMeanOfN1() {

    int[] values = { 1 };

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

    Assert.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 = 0;

    Assert.assertEquals(expectedValue, actualValue, delta);
}

@Test
public void testMeanOfN3() {

    int[] values = {};

    double expectedValue = 0;
    double actualValue = FuncLib.meanOfN(values);
    double delta = 0;

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

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 permet de trier un tableau d’entiers.
    • La fonction int[][] Arrays.copyOf(int[][] original, int newLength) de la bibliothèque standard de Java permet de redimensionner un tableau en copiant les valeurs du tableau original dans un nouveau tableau de taille différente (seule la première dimension peut changer).
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]);
    }    
}