Initiez-vous à la programmation ludique
Initiez-vous à la programmation ludique
Version en ligne :
[Link]
2 Principes de base 6
2.1 La philosophie Javascool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 Installation et lancement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Pour commencer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.4 Éléments de syntaxe élémentaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1
3.5.3 Exercice : Encore des étoiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.5.4 Exercice : Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6 Fil rouge : deviner un nombre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.6.1 Exercice : Nombre de coups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
3.6.2 Exercice : Faire jouer l’ordinateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5 Manipuler l’information 34
5.1 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
5.1.1 Exercices du tutoriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.1.2 Exercice : Avec des fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.1.3 Exercice : Pendu et Mastermind . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
5.2 Calculs entiers et réels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.2.1 Exercice du tutoriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
5.2.2 Exercice : Calcul du reste de la division euclidienne . . . . . . . . . . . . . . . . . . 39
5.2.3 Exercice : Calcul de la racine carrée approchée . . . . . . . . . . . . . . . . . . . . 39
5.3 Calcul binaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.3.1 Exercices du tutoriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.3.2 Exercice : Hexadécimal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.3.3 Exercice : Pair ou impair . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.4 Logique et booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.4.1 Exercices du tutoriel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.4.2 Exercice : Tableaux de booléens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.5 Bibliothèque mathématique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.6 Caractères et chaı̂nes de caractères . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
5.6.1 Exercice : retour sur le pendu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.6.2 Exercice : Conversions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
5.6.3 Exercice : Vérification de données . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6 Aspects avancés 42
6.1 Structures et classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
6.1.1 Exercice : triangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.1.2 Exercice : Date de naissance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.2 Sauvegarder des données dans un fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
6.2.1 Exercice : Création d’un fichier de carrés . . . . . . . . . . . . . . . . . . . . . . . . 46
6.2.2 Exercice : Lecture d’un fichier structuré . . . . . . . . . . . . . . . . . . . . . . . . 46
2
6.2.3 Exercice : High-scores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3 Données partagées, copies et effets de bord . . . . . . . . . . . . . . . . . . . . . . . . . . 46
6.3.1 Exercice : Trouvez les erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
6.3.2 Exercice : Enchaı̂nement de conditions . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.3.3 Exercice : plusieurs variables avec le même nom . . . . . . . . . . . . . . . . . . . . 49
6.4 Activités Javascool pour le dessin 2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
6.4.1 Exercice : Logo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.4.2 Exercice : Dessiner sur une image . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.4.3 Exercice : Manipulation d’images . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3
1 Préambule
1.1 Avertissement
Ce document est encore en développement, certaines parties sont en cours de rédaction. Toute re-
marque ou suggestion (ou correction des exercices !) est la bienvenue : [Link]@[Link]
1.2.2 Javascool
Comme on peut le lire sur son site Web 2 , Java’s Cool (alias Javascool) est un logiciel conçu
pour l’apprentissage des bases de la programmation. Il se base sur le logiciel Java, qui est le seul
élément qui doit être installé sur la machine pour permettre à Javascool de fonctionner. Les avantages
de Javascool pour l’apprentissage de l’algorithmique et de la programmation sont multiples :
– L’utilisation de la syntaxe de Java, elle-même proche de la syntaxe de nombreux langages tels que le
C/C++, permet de donner aux élèves des bases qu’ils retrouveront fréquemment s’ils poursuivent
des études dans le domaine Informatique.
– Cette syntaxe est également assez proche des méta-langages (ou “langages algorithmiques”) tra-
ditionnellement utilisés pour enseigner l’algorithmique, par conséquent la traduction en langage
Javascool d’un algorithme exprimé en méta-langage est plus simple qu’avec d’autres langages,
même si ce document montre justement qu’il y a quelques pièges à éviter...
– Java étant multi-plateformes, Javascool peut fonctionner indifféremment sous Linux, Windows ou
Mac
1. [Link]
2. [Link]
4
– C’est un logiciel “tout-en-un”, intégrant à la fois un éditeur pour écrire ses programmes et une
fenêtre pour les exécuter
– Javascool est utilisé par de nombreux enseignants, et commence à proposer un nombre important
de ressources pédagogiques accessibles sur son site web
On peut néanmoins lui trouver quelques inconvénients :
– Les messages d’erreurs ne sont pas toujours “parlants”, et le logiciel ne propose pas réellement de
fonctionnalités pour faciliter l’élimination des erreurs (ce qui est parfois vu comme un avantage
étant donné que cela oblige à se poser plus de questions)
– Javascool contient des fonctionnalités permettant de simplifier l’apprentissage de la programmation
en “cachant” certaines fonctionnalités du langage Java ; cela peut entraı̂ner une confusion chez
certains élèves lorsqu’ils sont confrontés plus tard dans leur cursus à des langages réellement utilisés
dans le monde industriel ou académique.
– Java reste un langage relativement peu rapide par rapport à d’autres, ce qui n’empêche quand
même pas de l’utiliser même pour résoudre des problèmes complexes.
Il n’en reste pas moins que tout langage de programmation a ses avantages et inconvénients, et le choix
n’est pas facile parmi les milliers de langages utilisés actuellement sur la planète. Enseigner comment
traduire un algorithme dans un langage relativement simple comme Javascool permet de toute à façon
de préparer les élèves à apprendre plus tard comment passer facilement d’un langage de programmation
à un autre et comment choisir le langage le plus approprié selon le type d’application envisagé, comme
tout bon programmeur qui se respecte.
On peut aussi considérer Javascool comme un tremplin vers des plateformes de programmation pro-
fessionnelles comme Eclipse 3 ou Netbeans 4 .
5
les mathématiques ou la physique. Programmer un jeu de type “casse-briques” par exemple implique de
comprendre les notions de vélocité et de collision. On peut retrouver en fait dans les jeux un panorama
complet des notions abordées en informatique :
– Algorithmique classique (comment fonctionne le jeu, que se passe-t-il si le joueur entre telle ou telle
valeur)
– Structures de données (comment stocker et accéder efficacement aux données du jeu telles que les
scores ou les positions des joueurs)
– Affichage, graphismes 2D ou 3D (comment afficher et éventuellement dessiner des données à l’écran)
– Son (comment générer des sons au cours du jeu)
– Aide à la décision (comment programmer un adversaire performant face au joueur humain)
– Réseaux (comment faire communiquer des joueurs au travers du jeu)
– etc.
2 Principes de base
2.1 La philosophie Javascool
Javascool est plus qu’un langage de programmation : comme mentionné plus haut c’est un logiciel
complet intégrant un éditeur pour écrire ses programmes et une fenêtre pour les exécuter. De façon plus
générale, Javascool se veut un support permettant d’apprendre à la fois l’algorithmique et sa traduction
immédiate, en permettant au professeur de faciliter la vie des élèves grâce à la possibilité d’utiliser des
“proglets” existants ou qu’il programmera lui-même. Une “proglet” est un ensemble de fonctionnalités
écrites en Javascool et de documents expliquant comment les utiliser dans le cadre d’une activité, en
passant par une interface graphique ou en écrivant du code Javascool 5 .
Ainsi, on peut éventuellement proposer à l’élève d’utiliser une fonctionnalité (par exemple la fonction
load permettant de charger une image en mémoire) en lui cachant les détails concernant la façon dont
cette fonctionnalité est programmée.
Cette “philosophie” Javascool permet de simplifier la prise en main par l’élève de certains aspects
techniques pour qu’il puisse se concentrer sur les aspects purement algorithmiques. Pour reprendre l’ex-
emple de la manipulation d’images, l’élève pourra ainsi manipuler une image comme un tableau à deux
dimensions dont les cases représentent les intensités des pixels, sans avoir à comprendre comment sont
réellement codées les images au format JPG ou autre.
Nous nous concentrons dans ce document sur les aspects techniques de Javascool : la syntaxe du
langage (similaire à celle du langage Java), les fonctionnalités du logiciel, les causes les plus fréquentes
d’erreurs de traduction entre version algorithmique et version Javascool d’un programme. Les aspects plus
5. Les “educlets” sont des “proglets” restreintes à l’utilisation d’une interface graphique
6
Figure 1 – Fenêtre de lancement de Javascool
abstraits orientés vers la conception des algorithmes eux-mêmes, écrits dans un langage algorithmique,
ne sont pas traités dans ce document.
7
Figure 2 – Présentation des éléments de l’interface Javascool
3.1 HelloWorld
En cliquant sur “séquences d’instructions” on découvre le tutoriel “HelloWorld”, qui montre le pro-
gramme le plus simple que l’on peut écrire avec Javascool :
void main() {
println("Hello World !");
}
Ce programme correspond, en langage algorithmique, à :
Algorithme principal
Début
Afficher("Hello World !")
Fin
Vous pouvez copier le code de ce programme Javascool dans l’éditeur à gauche (en le retapant entièrement
ou en utilisant Ctrl-C puis Ctrl-V), puis cliquer sur “Compiler”, ce qui nécessite de sauvegarder votre
premier fichier Javascool. Une fois l’emplacement du fichier choisi, la console doit afficher “Compila-
tion réussie !”.
6. Et par convention, ces identificateurs commencent en général par une minuscule.
8
Reste enfin à exécuter le programme (en cliquant sur “Exécuter” donc), pour voir s’afficher le texte
voulu dans la console.
3.2 Variables
Revenir sur le “Parcours d’initiation”, cliquer sur “Page initiale” puis “variables”. On trouve ici la
traduction de l’algorithme :
Algorithme principal
Variable texte: cha^ ıne de caractères
Début
Afficher("Bonjour, quel est ton nom ?")
Saisir(texte)
Afficher("Enchanté ")
Afficher(texte)
Afficher(", et ... à bient^
ot !")
Fin
qui s’écrit en langage Javascool :
void main() {
println("Bonjour, quel est ton nom ?");
String texte = readString();
println("Enchanté " + texte + ", et ... à bient^
ot !");
}
La “variable” dont il est question dans ce tutoriel s’appelle ici texte.
Comme on le voit en compilant puis en exécutant ce programme, une petite fenêtre “Entrée au
clavier” s’ouvre pour permettre la saisie de texte, qui est utilisée ensuite pour l’affichage. Cet affichage
avec la fonction println implique un retour à la ligne. L’opérateur “+”, quand il s’applique à un affichage
(une ou plusieurs variables ou bien une phrase entourée par des guillemets) sert à créer une seule chaı̂ne
de caractères à partir de ces données : on parle alors de concaténation.
Alternativement, la fonction print, sans retour à la ligne, peut être utilisée pour obtenir le même
résultat sans nécessiter de concaténation :
void main() {
println("Bonjour, quel est ton nom ?");
String texte = readString();
print("Enchanté ");
print(texte);
println(", et ... à bient^
ot !");
}
9
3.2.1 Exercice : Afficher une ligne vide
Modifier le programme précédent pour faire afficher une ligne vide juste avant la dernière ligne
Algorithme principal
Variables rayon, circ, aire: nombres réels
Constante Pi: nombre réel <- 3.14159
Début
Afficher("Entrer le rayon: ")
Saisir(rayon)
circ <- 2 * rayon * Pi
aire <- Pi * rayon^2
Afficher("Circonférence du cercle de rayon ", rayon, ": ")
Afficher(circ)
Afficher("Aire: ")
Afficher(aire)
Fin
Attention, pour calculer le carré d’un nombre vous devez le multiplier par lui-même.
On ne vérifiera pas si le déterminant est réellement positif. Que se passe-t-il si ce n’est pas le cas ?
10
Algorithme principal
Variable temperature: réel
Début
Afficher("Entrer la température: ")
Saisir(temperature)
Si (temperature < 15) Alors
Afficher("allumer chauffage")
FinSi
Sinon
Si (temperature > 100) Alors
Afficher("appeler les pompiers")
FinSi
Sinon
Afficher("ne rien faire")
FinSinon
FinSinon
Fin
se traduira en Javascool par :
void main() {
println("Entrer la température: ");
double temperature = readFloat();
if(temperature < 15) {
println("allumer chauffage");
}
else if(temperature > 100) {
println("appeler les pompiers");
}
else {
println("ne rien faire");
}
}
On peut vérifier en compilant puis en exécutant ce nouveau programme qu’il change d’affichage en
fonction de la valeur saisie pour la température.
Le tutoriel permet ensuite de comprendre comment traduire les conditions d’égalité entre nombres
ou entre chaı̂nes de caractères, et également comment on peut combiner plusieurs conditions pour en
obtenir une seule. Par exemple l’algorithme :
Algorithme principal
Variable temperature: réel
Début
Afficher("Entrer la température: ")
Saisir(temperature)
Si ((temperature > 15) ET (temperature < 21)) Alors
Afficher("Température idéale !")
FinSi
Fin
se traduira par :
void main() {
println("Entrer la température: ");
double temperature = readFloat();
if((temperature > 15) && (temperature < 21)) {
println("Température idéale !");
}
}
11
Il est important de remarquer que les conditions d’égalité stricte ont des formes particulières, d’abord
l’égalité entre deux nombres : le test écrit en langage algorithmique “Si (x = 2) Alors ...” se traduira
par if (x==2) .... Cette double égalité est souvent cause d’erreurs dans d’autres langages proches de
la syntaxe de Java, notamment C et C++. Pour l’égalité entre chaı̂nes de caractères, on traduira “Si
(texte=”Bonjour”) Alors ...” par if equal(texte,”Bonjour”) ....
Un autre élément remarquable dans les exemples ci-dessus tient à l’utilisation des espaces en début
de ligne, permettant de décaler les instructions quand on entre dans un bloc correspondant à un “Alors”
ou un “Sinon”, ce qui donne tout de suite une vision claire du code bien utile pour le débogage. Ce
point est parfois fastidieux à respecter, heureusement il est possible de laisser Javascool faire le travail
en cliquant sur le bouton “Reformater le code”. N’hésitez pas à l’utiliser !
La dernière partie du tutoriel aborde la notion de variable booléenne, qui ne peut prendre comme
valeur que true ou false. Un lien cliquable après le dernier exercice du tutoriel permet d’en savoir plus.
Enfin, on peut noter qu’il manque ici la définition de la structure switch (équivalent à SELON en
algorithmique), qui permet de ne pas avoir à enchaı̂ner les tests, comme dans l’exemple suivant :
void main() {
println("Entrez un numéro de mois:");
int n = readInt();
switch (n) {
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
println("Mois à 31 jours");
break;
case 4:
case 6:
case 9:
case 11:
println("Mois à 30 jours");
break;
case 2: println("Mois à 28 ou 29 jours");
break;
default: println("Erreur de saisie");
}
}
Le mot-clef break permet de séparer les cas, comme ici pour traiter de façon unique les mois à 30 ou
31 jours.
12
Début
Afficher("Entrer a, b et c")
Saisir(a,b,c)
Si (a < b < c) Alors Afficher("a < b < c")
FinSi
Sinon Si (a < c < b) Alors Afficher("a < c < b")
FinSi
Sinon Si (b < a < c) Alors ...
...
Fin
3.4 Fonctions
Le tutoriel “Fonctions” de Javascool permet de bien comprendre la notion de structuration d’un
programme en différentes fonctions, pour le rendre plus lisible et plus facilement ré-utilisable. Il est
notamment important de noter que nous avons en fait déjà utilisé des fonctions prédéfinies de Java :
println, pow, etc.
Nous insistons ici encore une fois sur la traduction d’une fonction écrite en langage algorithmique, en
reprenant la fonction int abs(int x) décrite au début du tutoriel. L’algorithme suivant :
Fonction abs
Entrée: x: entier
Sortie: entier
Début
Si (x > 0) Alors Retourner x
Sinon Retourner -x
Fin_abs
Procédure affichage_abs
Entrée: x: entier
Début
Afficher("Valeur absolue: ", abs(x))
Fin_affichage_abs
Algorithme principal
Variable y: entier
Début
Afficher("Entrer y: ")
Saisir(y)
affichage_abs(y)
Fin
13
void affichage_abs(int x) {
println("Valeur absolue: " + abs(x));
}
void main() {
println("Entrer y: ");
int y = readInteger();
affichage_abs(y);
}
On voit dans cet exemple plusieurs éléments remarquables :
– une même variable x peut être utilisée dans deux fonctions différentes, ce sont deux variables
différentes qui n’ont aucun rapport entre elles pour l’ordinateur : on dit que ces variables sont
locales à la fonction qui les déclare
– il peut y avoir zéro, une ou plusieurs entrées ; en revanche il y a zéro ou une sortie au maximum
– les variables d’entrée sont placées entre parenthèses après le nom de la fonction, en précisant leur
type
– on parle généralement en algorithmique de procédure lorsqu’une fonction ne retourne pas de valeur
en sortie, ce qui se traduit par le mot-clef void placé devant le nom de la fonction
– comme indiqué dans le tutoriel, l’instruction void main() traduit par conséquent le fait que
l’algorithme principal ne prend aucune entrée, et qu’il ne retourne aucune valeur en sortie
Supposons maintenant que nous avons écrit une fonction int soustrait(int a, int b) qui retourne la
valeur a − b. Pour l’utiliser, nous devons faire attention à l’ordre dans lequel les données sont transmises
en entrée de la fonction. Par exemple, dans le programme ci-dessous on comprend aisément qu’il n’est
pas équivalent d’écrire soustrait(x1, x2) ou soustrait(x2, x1) :
int soustrait(int a, int b) {
return a - b;
}
void main() {
println("Entrer x1: ");
int x1 = readInteger();
println("Entrer x2: ");
int x2 = readInteger();
println("Résultat: " + soustrait(x1, x2));
}
14
Algorithme principal
Variables a, b, c, d: entiers
Début
Afficher("Entrer a, b et c")
Saisir(a,b,c)
d <- ordre(a,b,c)
Si (d = 1) Alors Afficher("a < b < c")
FinSi
Sinon Si (d = 2) Alors Afficher("a < c < b")
FinSi
Sinon Si ...
...
Fin
3.5 Boucles
Ce tutoriel montre deux formes de boucles, la forme while équivalente en langage algorithmique à
tant que, et la forme for équivalente à Pour (soit une traduction littérale).
La première forme permet, à partir de l’algorithme :
Algorithme principal
Variable n: entier
Début
n <- 0
Tant que (n < 10) Faire
Début
Afficher ("Hello World !")
n <- n + 1
FinTantQue
Fin
d’obtenir en JavaScool :
void main() {
int n = 0;
while( n < 10) {
println("Hello World !");
n = n + 1;
}
}
On voit que l’équivalence est réellement immédiate : si le principe algorithmique de la boucle Tant que
est acquis, alors son application en Javascool est très simple.
Les choses sont un peu plus compliquées en ce qui concerne la boucle Pour. Ici, la forme algorithmique
utilisant une boucle Pour équivalente à l’algorithme précédent sera :
Algorithme principal
Variable n: entier
Début
Pour n de 0 à 9 (par pas de 1) Faire
Début
15
Afficher ("Hello World !")
FinPour
Fin
ce qui se traduit en Javascool par :
void main() {
for(int n = 0; n < 10; n = n + 1) {
println("Hello World !");
}
}
On voit ici que l’équivalence n’est plus aussi immédiate : la boucle for est en fait une forme de boucle
permettant de compacter la forme while, mais on ne retrouve pas les notions de :
– initialisation automatique : en algorithmique le compteur n démarre à 0 par définition, alors qu’en
Javascool l’instruction int n=0 placée à l’intérieur de la boucle for est obligatoire
– comptage automatique : en algorithmique la boucle Pour autorise à définir le pas (de 1 en 1, 2
en 2, etc.), mais ceci nécessite en Javascool une instruction explicite n = n + 1 pour modifier la
valeur du compteur
– fin de boucle automatique : on considère que la répétition de la boucle Pour s’arrête lorsque le
compteur dépasse la valeur définie comme borne d’arrêt (fixée à 9 dans l’exemple ci-dessus). Par
contre, la forme for oblige à écrire un test permettant de vérifier que le compteur n’a pas atteint
cette valeur. Ici on aurait aussi pu écrire n <= 9 à la place de n < 10
Une boucle for est en fait définie de façon générale par 3 types d’instructions, placées entre parenthèses
et séparées par des points-virgules :
for(initialisation; test_de_fin; increment_avant_de_recommencer) {
corps_de_la_boucle
}
Comme l’écrit l’auteur du tutoriel, la forme for “est plus concise... mais ne change rien sur le fond”. Elle
a en effet été introduite dans les langages de programmation plutôt comme un moyen d’écrire plus vite
du code. Il est toujours possible de ré-écrire avec la forme while un programme écrit initialement avec
for, et inversement.
Néanmoins, la boucle for étant souvent utilisée par les programmeurs, il est utile de s’y famil-
iariser pour comprendre comment fonctionnent des programmes existants. On peut aussi mentionner
l’incrémentation n++, équivalente en Javascool à n=n+1. Ceci n’est pas spécifique aux boucles mais
on trouvera souvent le programme précédent plutôt écrit sous la forme :
void main() {
for(int n = 0; n < 10; n++) {
println("Hello World !");
}
}
16
3.5.2 Exercice : Vérifier les entrées
Les boucles sont très utiles pour vérifier les entrées d’un programme, et faire en sorte qu’elles soient
correctes. Traduire par exemple l’algorithme ci-dessous :
Algorithme principal
Variables rayon, circ, aire: nombres réels
Constante Pi: nombre réel <- 3.14159
Début
Afficher("Entrer le rayon: ")
Saisir(rayon)
Tant que (rayon < 0) Faire
Début
Afficher("Erreur, rayon négatif. Entrer le rayon: ")
Saisir(rayon)
FinTantQue
circ <- 2 * rayon * Pi
aire <- Pi * rayon^2
Afficher("Circonférence du cercle de rayon ", rayon, ": ")
Afficher(circ)
Afficher("Aire: ")
Afficher(aire)
Fin
En suivant le même principe, modifier le programme écrit pour l’exercice 3.2.5 de façon à obliger l’util-
isateur à rentrer des coefficients a, b et c donnant un déterminant positif.
17
// Fonction permettant de saisir un nombre entre 1 et 1000
int saisieUnMille ()
{
print("Entrez un nombre: ");
int n = readInt();
while ((n < 1) || (n > 1000)) {
println("Erreur, nombre hors de l’intervalle [0;1000]");
print("Entrez un nombre: ");
n = readInt();
}
return n;
}
// Programme principal
void main ()
{
int nbemystere = random(0,1000);
int x = saisieUnMille ();
while (x != nbemystere) {
if (x < nbemystere) {
println(x+" est trop petit...");
}
else {
println(x+" est trop grand...");
}
x = saisieUnMille ();
}
println("Bravo, c’était bien "+x);
}
On voit ici apparaı̂tre tous les éléments vu jusqu’ici : affichage à l’écran et saisie au clavier, variables,
tests, fonctions et boucles.
18
tivement testé toutes les possibilités d’erreur ou de comportement non prévu en raison de la lenteur et
du coût de fonctionnement des ordinateurs. Aujourd’hui, la rapidité d’exécution du moindre ordinateur
personnel fait qu’il est au contraire conseillé d’utiliser l’ordinateur comme un outil de mise au point,
sans forcément passer par une phase de test manuelle (sur papier). Pour autant, il est parfois difficile
d’enseigner des concepts liés à l’algorithmique directement sur machine, tant les élèves ont tendance à se
focaliser sur leur écran en n’écoutant pas ou peu les consignes : ainsi, il ne faut pas négliger les séances
sans ordinateur pendant lesquelles l’élève doit imaginer le comportement de ses algorithmes et de leurs
traductions sur machine, ce qui permet justement de se mettre à à la place de l’ordinateur et de mieux
comprendre son fonctionnement. Le site web de Javascool propose des activités “unplugged” adaptées à
ce type de fonctionnement 7 .
Un autre écueil de l’apprentissage de la programmation sur machine vient du fait que, dès qu’une
erreur est rencontrée, certains élèves ont tendance à corriger au hasard, sans essayer d’utiliser les in-
formations pertinentes délivrées ou qui pourraient être délivrées par l’ordinateur. D’autres s’arrêtent
tout simplement, estimant que c’est la machine qui commet une erreur. Il peut également arriver que
la correction d’erreurs ne soit pas pertinente, et change complètement le comportement du programme
ensuite, même si celui-ci fonctionne.
Il est d’ailleurs difficile d’inculquer aux élèves la notion de vérification, consistant à tester si le
comportement d’un programme est conforme à celui attendu dans différentes situations. Par exemple,
que se passe-t-il si lors de la saisie d’une valeur l’utilisateur entre une valeur négative (comme à l’exercice
3.2.3) ? Cela a-t-il été prévu par le programme ? Si non, ne risquons-nous pas d’avoir des problèmes si
nous reprenons le code plus tard dans un autre programme ? Ce type de questionnement, même si le
but n’est pas de former des informaticiens directement opérationnels après le bac, reste intéressant pour
l’apprentissage de l’informatique.
Par conséquent, il est souvent pertinent de proposer des exercices où l’on doit essayer de trouver, sans
ordinateur, les erreurs qui pourraient se produire. Nous proposons ci-dessous un récapitulatif d’erreurs
classiquement rencontrées par les débutants, sans toutefois prétendre à l’exhaustivité. Ces exemples
peuvent servir de supports d’exercices, mais ils vous seront surtout utiles pour votre propre apprentissage
de Javascool.
void main() {
double x = 2y;
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 3 :
Un ’;’ est attendu (il peut manquer, ou une parenthèse ^
etre incorrecte, ..)
double x = 2y;
^
Ici l’erreur vient de l’instruction x=2y ;, qui suit la notation habituellement utilisée en mathématiques
mais qui devrait s’écrire en Javascool x=2*y ; et pourtant, le compilateur indique :
7. [Link]
8. C’est en fait un peu plus compliqué dans le cas de Javascool : un programme Javascool est d’abord traduit en pro-
gramme Java, et c’est ce dernier qui est compilé. Pour plus d’explications concernant Java voir : [Link]
com/cours/
19
– qu’à la ligne 3 un point-virgule est attendu (Javascool ajoute une première ligne vide quand on
reformate le code)
– que ce point-virgule devrait être placé après x (le symbole ^ étant censé indiquer l’endroit exact
de l’erreur sur la ligne).
Le compilateur considère en fait qu’il faudrait plutôt écrire x ;, ce qui est effectivement un moyen de
corriger l’erreur mais ne répond probablement pas à ce que souhaitait le concepteur... qui a de toute façon
oublié de déclarer la variable y : après avoir remplacé l’instruction fautive par x=2*y ; une nouvelle
erreur apparaı̂tra à la compilation.
Par conséquent l’information la plus utile reste le plus souvent le numéro de ligne, mais même cette
information peut être trompeuse comme ci-dessous où le compilateur indique une erreur à la ligne 4 alors
que celle-ci est clairement due à une parenthèse manquante au début :
void main( {
double x = 2*y;
}
-- Affichage de la sortie --------
Attention: il faut un block "void main()" pour que le programme puisque se compiler
4.1.1 Points-virgules
Pas ambiguı̈té ci-dessous, le point-virgule manquant en fin de ligne est à l’origine de l’erreur, et il est
correctement détecté :
void main() {
println("Hello World !")
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 3 :
Un ’;’ est attendu (il peut manquer, ou une parenthèse ^
etre incorrecte, ..)
println("Hello World !")
^
Pour résoudre le problème la tentation est grande d’ajouter systématiquement un point-virgule à la
fin de chaque ligne. Parfois ce n’est pas très grave, par exemple le programme ci-dessous n’entraı̂nera
pas d’erreurs :
void main() {;
println("Hello World !");;
};
20
Nous verrons dans la partie 4.2 que cela peut tout de même devenir problématique dans certains cas et
que ce n’est pas un comportement à encourager.
Void Main() {
println("Hello World !");
}
-- Affichage de la sortie --------
Attention: il faut un block "void main()" pour que le programme puisque se compiler
void main() {
printl("Hello World !");
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 3 :
Il y a un symbole non-défini à cette ligne : method printl([Link])
(utilisez-vous la bonne proglet ?)
printl("Hello World !");
^
void main()
println("Hello World !");
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 2 :
Un ’;’ est attendu (il peut manquer, ou une parenthèse ^
etre incorrecte, ..)
void main()
^
Ci-dessous il manque une accolade fermante, ce qui est relativement bien détecté par le compilateur
qui indique qu’il a analysé (ou parsé) le code et qu’il a atteint la fin du fichier sans rencontrer cette
accolade pourtant nécessaire :
void main() {
println("Hello World !");
-- Affichage de la sortie --------
Erreur de syntaxe ligne 5 :
21
reached end of file while parsing
}
^
Sur le même principe, attention à ouvrir et fermer correctement les parenthèses.
void main() {
x = 2;
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 3 :
Il y a un symbole non-défini à cette ligne : variable x
(utilisez-vous la bonne proglet ?)
x = 2;
^
De la même façon, il n’y aucun sens à afficher le contenu de la variable x si on ne lui pas affecté
préalablement une valeur. C’est un problème classique d’initialisation de variable :
void main() {
int x;
println(x);
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 4 :
variable x might not have been initialized
println(x);
^
Attention toutefois à ne pas déclarer plusieurs fois la même variable :
void main() {
int x = 2;
int y = x+x;
int x = 3;
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 5 :
x is already defined in main()
int x = 3;
^
Il n’y a aucun sens à vouloir affecter à une variable une valeur non-adaptée. Ci-dessous par exemple
on affecte à x le résultat donné par l’instruction println(”Hello World !”), alors que println ne sert
qu’à afficher du texte à l’écran :
void main() {
int x = println("Hello World !");
}
-- Affichage de la sortie --------
22
Erreur de syntaxe ligne 3 :
Vous avez mis une valeur de type void alors qu’il faut une valeur de type int
int x = println("Hello World !");
^
De façon similaire, il faut être très vigilant avec les types numériques qui ne sont pas équivalents entre
eux :
void main() {
double x = 2;
int y = 2*x;
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 4 :
possible loss of precision
found : double
required: int
int y = 2 * x;
^
Dans cet exemple l’erreur est réellement difficile à comprendre : il n’y a clairement ici aucun risque de
perte de précision (possible loss of precision) avec l’opération y=2*x puisque x a été initialisé avec une
valeur entière. Mais ce serait le cas si on avait écrit x=2.5, et le compilateur ne veut donc prendre aucun
risque...
4.1.5 Comparaisons
Le test d’égalité en Javascool s’écrit avec un double symbole = (ci-dessous il faudrait donc plutôt
écrire x==3). Si ce n’est pas le cas le compilateur indique une erreur relativement compréhensible : il
indique que le résultat attendu pour une comparaison est plutôt de type boolean, c’est-à-dire une valeur
vraie ou fausse.
void main() {
int x = 2;
if (x = 3) {
println("Erreur");
}
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 4 :
Vous avez mis une valeur de type int alors qu’il faut une valeur de type boolean
if (x = 3) {
^
Et bien sûr attention à comparer ce qui est comparable, ici on essaie de comparer sans succès un
nombre avec du texte :
void main() {
int x = 2;
if (x == "Hello world !") {
println("Erreur");
}
}
-- Affichage de la sortie --------
Erreur de syntaxe ligne 4 :
23
incomparable types: int and [Link]
if (x == "Hello world !") {
^
24
Et pourtant la division par zéro n’est parfois pas détectée... Si dans les exemples précédents l’instruc-
tion incriminée est facile à déterminer, cela peut être plus compliqué :
void main() {
double x = 5/0.0;
println("x = "+x);
}
-- Affichage de la sortie --------
x = Infinity
On voit ici que la division par zéro n’a pas provoqué l’arrêt du programme, qui a bien exécuté l’instruction
5/0.0 puis affiché la valeur obtenue. La différence avec les exemples précédents est que le type double de
Javascool intègre une dimension symbolique : une variable de type double peut avoir la valeur Infinity,
ce qui est cohérent avec la définition mathématique de la division. Et on peut inversement... obtenir la
valeur zéro en divisant par Infinity, comme le montre l’exemple suivant.
void main() {
double x = 5/0.0;
println("x = "+x);
double y = 3 + x;
println("y = "+y);
double z = 1 / y;
println("z = " + z);
}
-- Affichage de la sortie --------
x = Infinity
y = Infinity
z = 0.0
25
}
-- Affichage de la sortie --------
2 12 182 33672 1133904602 1044523572 -1265919698
Là encore l’erreur n’est pas signalée, au programmeur donc de faire attention. Par définition les valeurs
pour les int vont de −2147483648 à 2147483647, et pour les double de 4, 9.10−324 à 1, 7.10308
void main() {
int x = 0;
for (int i = 1; i <= 7; i++); {
x = (x+1)*(x+2);
print(x+" ");
}
}
-- Affichage de la sortie --------
2
Comme on le voit, la boucle for est ici terminée ici par un point-virgule. Ceci entraı̂ne une exécution
donnant la fausse impression que la boucle n’est exécutée que pour la première valeur de i. Pourtant,
le point-virgule a comme conséquence que la boucle “tourne” bien jusqu’à i = 7, mais cela n’a comme
conséquence que l’incrémentation du compteur. Le bloc de calcul de la valeur de x n’est en fait exécuté
qu’une fois la boucle terminée, et ce code Javascool aurait plutôt comme équivalent en langage algorith-
mique :
Algorithme principal
Variable x: entier
Début
x <- 0
Pour i de 1 à 7 Faire
Début
// Bloc vide !
FinPour
x <- (x+1)*(x+2)
Afficher (x, " ")
Fin
On observe le même problème dans le cas d’un test, comme dans l’exemple suivant :
void main() {
int x = 0;
if (x == 3); {
print(x+" ");
}
}
-- Affichage de la sortie --------
0
Encore une fois, le comportement qui semble attendu (afficher la valeur de x si celle-ci est égale à 3) n’est
pas celui observé, ce qui est dû au point-virgule en fin de test. L’équivalent en langage algorithmique est
plutôt ici :
26
Algorithme principal
Variable x: entier
Début
x <- 0
Si (x = 3) Alors
Début
// Bloc vide !
FinSi
Afficher (x, " ")
Fin
Il est évident que ce comportement n’est pas en général celui désiré, par conséquent les points-
virgules en fin de boucle ou de test doivent la plupart du temps être évités.
27
0
1
2
...
-- Seconde traduction erronée en Javascool
void main() {
int x = 0;
while (x < 10) {
println(x+" ");
}
}
-- Affichage de la sortie --------
0
0
0
...
Dans le premier cas, le test utilisé pour terminer la boucle est mal traduit, et implique que celle-ci ne
s’arrêtera jamais puisque la condition x >= 0 est toujours vérifiée. Dans le second cas, c’est l’erreur
vient de l’oubli de l’incrémentation de la variable. Encore une fois la boucle ne s’arrêtera jamais puisque
x reste toujours à 0 et la condition x < 10 n’a aucune chance de ne plus être vérifiée.
Évidemment les cas les plus complexes à résoudre sont ceux impliquant d’autres types d’erreurs, par
exemple le point-virgule en fin de test :
-- Troisième traduction erronée en Javascool
void main() {
int x = 0;
while (x < 10); {
println(x+" ");
x++;
}
}
Ici le point-virgule fait que la boucle reste bloquée puisque le compteur x n’est jamais incrémenté, par
conséquent rien ne s’affiche à l’écran...
28
4.3 Mise au point de programmes
La plupart des langages de programmation disposent de logiciels appelés EDI (pour Environnement
de développement intégré) tels que Visual Studio, Eclipse, Netbeans, etc. Ces logiciels permettent d’écrire
des programmes tout en réduisant le risque d’erreur, car ils proposent notamment une aide lors de la
saisie à la manière d’un correcteur orthographique dans un logiciel de traitement de texte. Ils sont aussi
plus complexes à utiliser que Javascool, qui offre moins de fonctionnalités de mise au point mais permet
néanmoins d’aider à la recherche d’erreurs. Il faut de plus essayer, pour tout développement informatique,
de se tenir à des règles simples qui aideront beaucoup à se rapprocher du programme final désiré.
29
-------------------
On voit ici que si la condition (d! = 0) n’est pas vérifiée, alors le programme s’arrête en affichant
le message spécifié par le programmeur. Si au contraire cette condition est satisfaite alors l’exécution
continuera normalement.
void main() {
30
long heureDebut = [Link]();
for (int i = 1; i < 5; i++) {
double test = random()*1000;
println("Racine de "+test+" calculée avec sqrt: "
+sqrt(test));
println("Racine de "+test+" calculée avec racine: "
+racine(test));
}
long heureFin = [Link]();
println("Temps d’exécution: "+ (heureFin - heureDebut)+ "ms");
}
-- Affichage de la sortie --------
Racine de 876.1992731341172 calculée avec sqrt: 29.60066339010187
...
Temps d’exécution: 101ms
Le principe est de stocker dans une variable de type long la valeur de l’horloge de l’ordinateur
avant le démarrage des calculs, puis de ré-itérer cette opération à la fin. Ainsi, en faisant la différence
entre ces deux valeurs on obtient le temps, exprimé en ms, qui s’est écoulé entre ces deux opérations.
Il est important de noter cependant que ce temps peut être plus ou moins long en fonction d’autres
événements, par exemple si de nombreux logiciels sont en train de fonctionner sur l’ordinateur, ou bien si
celui-ci est entré en mode “mise à jour”, etc. Par conséquent il est probable que si on exécute de nouveau
ce programme le temps d’exécution sera proche de 101ms mais pas tout à fait identique.
On peut évidemment s’intéresser seulement à certaines parties du programme, par exemple ici pour
déterminer si le calcul de la racine carrée est plus rapide avec sqrt ou avec notre fonction racine :
void main() {
long heureDebut, heureFin;
for (int i = 1; i < 5; i++) {
double test = random()*1000;
heureDebut = [Link]();
println("Racine de "+test+" calculée avec sqrt: "
+sqrt(test));
heureFin = [Link]();
println("Temps d’exécution pour sqrt: "+ (heureFin - heureDebut)+ "ms");
heureDebut = [Link]();
println("Racine de "+test+" calculée avec racine: "+racine(test));
heureFin = [Link]();
println("Temps d’exécution pour racine: "+ (heureFin - heureDebut)+ "ms");
}
}
-- Affichage de la sortie --------
Racine de 210.81480147628383 calculée avec sqrt: 14.519462850817996
Temps d’exécution pour sqrt: 14ms
Racine de 210.81480147628383 calculée avec racine: 14.519463472506295
Temps d’exécution pour racine: 73ms
Racine de 186.84659004566151 calculée avec sqrt: 13.669183956830105
Temps d’exécution pour sqrt: 4ms
Racine de 186.84659004566151 calculée avec racine: 13.669184151203385
Temps d’exécution pour racine: 3ms
Racine de 352.80059504672334 calculée avec sqrt: 18.782986851050165
Temps d’exécution pour sqrt: 3ms
Racine de 352.80059504672334 calculée avec racine: 18.78303152345477
Temps d’exécution pour racine: 3ms
...
On peut remarquer ici que l’exécution lors des premiers calculs semble prendre plus de temps. Il s’agit en
fait d’opérations d’initialisations exécutées par Javascool, impossibles à maı̂triser par le programmeur.
31
On voit ensuite que les temps de calculs sont semblables, on pourrait donc dire que les deux calculs sont
équivalents. Pourtant, considérons ce nouvel exemple :
void main() {
long heureDebut, heureFin;
long tpssqrt, tpsracine;
tpssqrt = tpsracine = 0;
for (int i = 1; i < 500000; i++) {
double test = random()*1000;
heureDebut = [Link]();
double valsqrt = sqrt(test);
heureFin = [Link]();
tpssqrt += (heureFin - heureDebut);
heureDebut = [Link]();
double valracine = racine(test);
heureFin = [Link]();
tpsracine += (heureFin - heureDebut);
}
println("Temps d’exécution total pour sqrt: " + tpssqrt + "ms");
println("Temps d’exécution total pour racine: " + tpsracine + "ms");
}
-- Affichage de la sortie --------
Temps d’exécution total pour sqrt: 685ms
Temps d’exécution total pour racine: 620ms
Ce dernier exemple donne des résultats plus significatifs, d’abord en enlevant du calcul de vitesse les
opérations d’affichage à l’écran, et en considérant un grand nombre d’opérations (500000). Par conséquent
on peut conclure que racine fonctionne de façon légèrement plus rapide que sqrt, ce qui est relativement
normal puisque notre calcul de racine carrée renvoie des résultats un peu moins précis.
32
}
return res;
}
void main ()
{
int toto = random(0,1000);
int tutu = saisieUnMille ();
while (tutu != toto) {
if (tutu < toto) {
println(x+" est trop petit...");
}
else {
println(tutu+" est trop grand...");
}
tutu = saisieUnMille ();
}
println("Bravo, c’était bien "+tutu);
}
En enlevant l’indentation (et en renommant les variables), on obtient un programme peu lisible, puisqu’il
est difficile de voir par exemple où s’arrête la boucle while. Ce problème est évidemment de plus en plus
critique à mesure qu’on augmente le nombre de lignes dans un programme.
Tout ceci n’est bien sûr pas propre à Javascool, et la rédaction de programmes dans un langage
quelconque devrait de toute façon respecter ces principes.
33
3. L’ordinateur vérifie le résultat. Si le joueur n’a pas trouvé la bonne réponse, un message d’encour-
agement s’affiche et le programme recommence à l’étape 1.
4. Si le joueur a bien trouvé la bonne réponse, l’ordinateur le félicite et affiche le temps mis pour
répondre (grâce à la fonction currentTimeMillis vue précédemment) avant de repartir à l’étape
1.
Améliorer ensuite en ajoutant la notion de high-score, c’est-à-dire stocker dans une variable le temps
de réponse du joueur le plus rapide, qui peut éventuellement être battu...
Une correction de cet exercice est proposée à la section 9.2.1.
5 Manipuler l’information
On s’intéresse ici à la manipulation avec Javascool d’entités algorithmiques usuelles (les tableaux
et les caractères), et d’autres éléments peu développés dans un cours d’algorithmique mais qui sont à
connaı̂tre pour mieux comprendre le fonctionnement de Javascool et pouvoir réaliser plus facilement ses
propres programmes. On insistera notamment ici sur les aspects liés à la représentation des nombres
(entiers ou réels), au calcul binaire et à la logique booléenne.
Les différents éléments développés ci-dessous sont accessibles à partir du tutoriel “Faire des exercices
d’application : les algorithmes mathématiques en 2nd” accessible sur [Link]
fr/documents/sujets-mathinfo/[Link].
5.1 Tableaux
Un tableau (appelé aussi vecteur) est un ensemble de valeurs du même type stockées les unes
à la suite des autres. Nous nous basons ici sur l’activité Javascool sur les tableaux accessible depuis le
tutoriel, qui s’ouvre par l’exemple suivant :
void main()
{
// Déclaration et remplissage du tableau "noms"
String[] noms = {"Alice", "Bob", "Samia", "Zheng-You"};
// Affichage du contenu du tableau (première méthode)
print("Contenu du tableau noms: ");
print(noms[0] + " ");
print(noms[1] + " ");
print(noms[2] + " ");
print(noms[3] + " ");
println("");
// Modification de l’une des valeurs
noms[2] = "Samiha";
// Affichage du contenu du tableau (deuxième méthode)
print("Contenu du tableau noms: ");
for (int i = 0; i < [Link]; i++) {
print(noms[i] + " ");
}
println("");
}
-- Affichage de la sortie --------
Contenu du tableau noms: Alice Bob Samia Zheng-You
Contenu du tableau noms: Alice Bob Samiha Zheng-You
On comprend avec cet exemple que :
– un tableau est déclaré avec son type (ici String), et peut être rempli directement avec des valeurs
(ici 4). On obtient donc ici un tableau de 4 chaı̂nes de caractères.
– les différentes valeurs stockées dans le tableau sont accessibles grâce à leur index, cet index
démarrant à 0 pour la première valeur, 1 pour la seconde, etc.
– on peut modifier très facilement une valeur stockée dans le tableau
34
– comme indiqué dans le tutoriel, la variable [Link] stocke de façon automatique le nombre
de valeurs du tableau (ici 4)
– les boucles, et notamment la boucle for, sont très bien adaptées à la manipulation des tableaux.
Dans cet exemple, on affiche les valeurs du tableau grâce à une boucle, sans avoir à se préoccuper
du nombre de valeurs.
Il faut également noter que les exemples présentés ici fonctionnent avec des tableaux contenant des
String, mais nous verrons dans les exercices qui suivent qu’on peut de la même façon manipuler des int,
double, etc.
Le tutoriel pointe un problème récurrent avec les tableaux qui concerne l’accès à une valeur qui
n’existe pas. En effet, le compilateur autorise les instructions du type noms[-1], mais ces instructions
vont générer des erreurs à l’exécution :
void main()
{
String[] noms = {"Alice", "Bob", "Samia", "Zheng-You"};
// Affichage de la valeur noms[-1]
println(noms[-1]);
}
-- Affichage de la sortie --------
Sauvegarde de [Link] ..
Compilation réussie !
----------------------------------
[Link]: -1
L’erreur signalée ici indique que l’index utilisé (−1) est “hors-limite” (ou out of bounds) : en effet l’index
doit être obligatoirement compris entre 0 (inclus) et [Link] (exclu). Cela n’a donc pas de
sens non plus de vouloir accéder à la valeur noms[4] ici.
Il est possible de déclarer un tableau sans le remplir immédiatement, mais dans ce cas on devra
indiquer le nombre de valeurs :
void main()
{
int i;
// Déclaration du tableau "noms"
String[] noms = new String[4];
// Remplissage par l’utilisateur
for (i = 0; i < [Link]; i++) {
println("Entrez la valeur d’index " + i + " : ");
noms[i] = readString();
}
// Affichage du contenu du tableau (deuxième méthode)
print("Contenu du tableau noms: ");
for (i = 0; i < [Link]; i++) {
print(noms[i] + " ");
}
println("");
}
-- Affichage de la sortie --------
Entrez la valeur d’index 0 : (on entre "Bill")
Entrez la valeur d’index 1 : ...
Entrez la valeur d’index 2 : ...
Entrez la valeur d’index 3 : ...
Contenu du tableau noms: Bill Steve Mark Georges
La manipulation de tableaux à travers des fonctions mérite un exemple pour bien remarquer qu’un
tableau passé en paramètre d’une fonction devient une variable d’entrée-sortie :
void remplissage (String tab[]) {
35
for (int i = 0; i < [Link]; i++) {
println("Entrez la valeur d’index " + i + " : ");
tab[i] = readString();
}
}
void affichage (String tab[]) {
for (int i = 0; i < [Link]; i++) {
print(tab[i] + " ");
}
println("");
}
void main()
{
// Déclaration du tableau "noms"
String[] noms = new String[4];
// Remplissage par l’utilisateur
remplissage (noms);
// Affichage du contenu du tableau
print("Contenu du tableau noms: ");
affichage (noms);
}
Le programme se comporte de la même façon que l’exemple précédent, mais une forme utilisant des
fonctions est préférable puisqu’elle permet une réutilisation plus simple du code déjà écrit : une fonction
réalisant un calcul complexe et mise au point pour un programme précis peut sûrement être reprise dans
un autre programme plus tard. On voit ici qu’un tableau passé en paramètre d’une fonction est bien
une variable d’entrée-sortie : tab est utilisé en entrée dans la fonction affichage (on affiche simplement
les valeurs qu’il contient) et en sortie dans la fonction remplissage (on modifie ces valeurs). Nous
reparlerons de cet aspect dans la section 6, mais il est important de noter que toute fonction comportant
la modification de valeurs d’un tableau doit être utilisée avec précaution, car ces modifications seront
effectives également en dehors de cette fonction.
L’exemple suivant garde l’affichage identique mais remplace le remplissage par une fonction équivalente :
String[] remplissage_bis (int n) {
String[] tab = new String[n];
for (int i = 0; i < [Link]; i++) {
println("Entrez la valeur d’index " + i + " : ");
tab[i] = readString();
}
return tab;
}
...
void main()
{
// Déclaration du tableau "noms"
String[] noms;
// Remplissage par l’utilisateur
noms = remplissage_bis(4);
...
}
On voit ici qu’une fonction peut également renvoyer un tableau. Dans ce cas, une différence notable avec
le programme précédent est que la déclaration du tableau noms dans la fonction main ne comporte pas
d’instruction à droite. Cette instruction de construction (ou allocation) est effectuée dans ce cas par la
fonction de remplissage. Une question subsidiaire : quelles seraient les valeurs affichées dans le cas d’un
tableau créé mais non rempli ?
Enfin, un dernier exemple pour montrer que les tableaux peuvent avoir autant de dimensions que
nécessaire. Voyons ici un programme qui affiche les valeurs d’une matrice h de taille 5x3 :
36
void main()
{
String[][] h = { {"#", " ", "#"},
{"#", " ", "#"},
{"#", "#", "#"},
{"#", " ", "#"},
{"#", " ", "#"}};
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 3; j++) {
print(h[i][j]);
}
println("");
}
}
-- Affichage de la sortie --------
# #
# #
###
# #
# #
Bien sûr les mêmes règles s’appliquent (notamment l’interdiction de sortir des limites du tableau) ; nous
verrons d’autres exemples d’utilisation des matrices dans la section 8.
37
5.2 Calculs entiers et réels
A partir du même tutoriel on peut accéder au sujet “Réaliser une calculette d’indice de masse cor-
porelle” et recopier le programme proposé dans l’éditeur. La formule est effectivement fausse, et doit être
remplacée par
imc=poids/(taille*taille). La question du type de la variable imc mérite quelques expérimentations.
Lorsqu’on utilise double le résultat est correct, ce qu’on peut vérifier en compilant et en exécutant le
programme : cliquer sur console et entrer les données 95 pour le poids et 1.81 pour la taille (et non 1, 81).
Un essai avec int génère une erreur dont l’intitulé est assez parlant : une possible perte de précision...
Cette erreur est en fait due à la division que l’on tente d’appliquer avec un entier comme numérateur (le
poids) et un réel comme dénominateur (la taille au carré), le résultat étant un entier.
Si l’on souhaite ne pas en tenir compte, il est possible de forcer la conversion entre entiers et réels,
grâce à int imc=poids/(int)(taille * taille); On convertit donc la taille au carré sous forme
d’entier avant d’appliquer la division. Comme prévu le résultat est faux, mais pourquoi ? On peut déjà
se rendre compte de l’erreur commise par la conversion en ajoutant une ligne qui affiche la valeur avant
et après :
38
5.2.2 Exercice : Calcul du reste de la division euclidienne
Il existe un moyen très simple de calculer le reste de la division de deux entiers, grâce à l’opérateur %,
appelé modulo. En écrivant par exemple int r=a%b où r, a et b sont des entiers, on obtient le reste r.
Écrire un algorithme permettant de faire le même calcul sans utiliser le modulo à partir de deux entiers
a et b saisis au clavier.
int x = 1352;
[Link]("x en base 10 (entier): %d, en base 10 (réel): %.2f, en hexa: %h",
x, (double)x, x);
-- Affichage de la sortie --------
x en base 10 (entier): 1352, en base 10 (réel): 1352,00, en hexa: 548
La fonction printf fonctionne en fait avec le principe du remplacement de motif : les données entre
guillemets sont affichées à l’écran, sauf celles commençant par % qui sont remplacées par les éléments
placés après les guillemets. Ici la variable x est utilisée 3 fois pour être affichée de 3 façon différentes (il
en existe d’autres). La forme hexadécimale est traditionnellement utilisée en informatique plutôt que la
base 10, notamment pour représenter les adresses en mémoire.
Un nombre en binaire n’étant constitué que de 0 et 1, on peut lui appliquer les opérateurs logiques
issus de l’algèbre de Boole, notamment la conjonction (“ET”) et la disjonction (“OU”). Les tables de
vérité 11 donnent le comportement de ces opérateurs qui s’appliquent en Javascool à des entiers binaires :
par exemple 0 ET 1 se note 0&1 en Javascool, et est égal à 0 (de la même façon la conjonction se note
—). On peut appliquer ces opérateurs à des entiers décimaux, l’opération s’effectuant sur chaque bit
composant leur représentation binaire. Par exemple, considérons l’opération 14 & 9 : (14)10 et 910 se
notent respectivement (1110)2 et (1001)2 , par conséquent le résultat est (1000)2 = 810 puisque de gauche
à droite 1&1 = 1, 1&0 = 0, 1&0 = 0 et 0&1 = 0
10. [Link]
11. [Link]
39
Il existe également des opérateurs de décalage, qui permettent de “pousser” les chiffres binaires vers
la gauche ou la droite, notés respectivement << et >>. Par exemple, 9 << 1 est égal à 18, puisque 910 =
(1001)2 et que le décalage d’un bit vers la gauche ajoute un bit 0 à droite, ce qui donne (10010)2 = (18)10 .
On peut de la même façon décaler de plusieurs bits à la fois.
boolean x = false;
boolean y = (1 < 2);
boolean z = (((!x) && y) || (x && (!y)));
println("x="+x+", y="+y+", z="+z);
if (z) {println("Z est vraie !!!");}
-- Affichage de la sortie --------
x=false, y=true, z=true
Z est vraie !!!
On voit ici la notion de valeur booléenne, essentielle en programmation et qui en Javascool peut donc
correspondre à true ou false stockée dans une variable de type boolean.
On peut remarquer qu’un if accepte uniquement comme prédicat une variable de type boolean, le
résultat d’une comparaison, ou bien directement une valeur true ou false. Cela n’a pas de sens par
exemple d’écrire dans un programme if (13)
40
5.4.2 Exercice : Tableaux de booléens
Reprendre les exercices de la section 5.3.1 en manipulant cette fois des tableaux de booléens et non
d’entiers. Voyez-vous l’intérêt d’une telle démarche ? Pensez-vous qu’elle pourrait s’appliquer aussi dans
le cas du jeu du pendu (section 5.1.3) ?
41
Encore quelques exemples (mais il existe de nombreuses autres fonctions applicables au type String 13 ) :
– indexOf(char c) retourne la position de la première occurrence du caractère c dans la chaı̂ne :
par ex. "bonjour".indexOf(’o’) renvoie 1
– replace(char oldChar, char newChar) renvoie une nouvelle chaı̂ne où toutes les occurrences
de oldChar sont remplacées par newChar : par ex. "bonjour".replace(’o’,’i’) renvoie ”bin-
jiur”
– substring(int beginIndex, int endIndex) renvoie la sous-chaı̂ne allant de beginIndex à
endIndex (exclus) : par ex. "bonjour".substring(1, 5) renvoie ”onjo”
– toLowerCase() et toUpperCase() renvoient une nouvelle chaı̂ne contenant la chaı̂ne initiale
convertie en minuscules ou majuscules.
6 Aspects avancés
6.1 Structures et classes
La structure en algorithmique (ou type composé, ou encore enregistrement) est un type défini par
l’utilisateur, permettant de regrouper au sein d’une même entité un ensemble de types de base. Un type
structuré peut donc être utilisé pour stocker des données évoluées, par exemple on pourra définir le type
cartegrise par :
structure carte_grise {
nom : chaine de caractère
prenom : chaine de caractère
marque : chaine de caractère
modele : chaine de caractère
immatriculation : chaine de caractère
annee_mise_en_service : entier
puissance_fiscale : entier
}
La traduction en Javascool de cet exemple est directe grâce au mot-clef class :
13. [Link]
42
class cartegrise {
String nom, prenom, marque, modele, immatriculation;
int annee_mise_en_service, puissance_fiscale;
}
Pour utiliser une variable d’un type structuré, il est nécessaire de la créer avec le mot-clef new :
void main()
{
cartegrise cg = new cartegrise();
[Link] = "Smith";
[Link] = "Bill";
...
println(cg);
}
-- Affichage de la sortie --------
my_class$cartegrise@3580ab
On voit ici le problème de l’affichage : Javascool n’est pas capable de déterminer ce que l’utilisateur
souhaite exactement, il se contente d’afficher l’adresse de la variable en mémoire. Heureusement un type
structuré peut s’accompagner de fonctions diverses, utilisables ensuite à la manière du type String,
notamment pour l’affichage. On peut aussi bien sûr définir des fonctions en dehors du bloc class :
class triangle {
double cote1, cote2, cote3;
void saisie() {
println("Saisir les valeurs des 3 cotés du triangle");
cote1 = readDouble();
cote2 = readDouble();
cote3 = readDouble();
}
public String toString() {
return ("Triangle de cotés "+cote1+", "
+cote2+", "+cote3);
}
boolean estEquilateral () {
return ((cote1 == cote2) && (cote2 == cote3));
}
}
void main()
{
triangle t = new triangle();
[Link]();
println(t);
if ([Link]()) {
println("Ce triangle est equilateral");
}
triangle u = t;
println("u et t identiques ? "+sontIdentiques(t,u));
}
La fonction toString a une signature particulière qu’il faut respecter exactement comme sur l’exemple :
c’est cette forme qui permet ensuite de pouvoir afficher les informations que l’on souhaite sur la variable
43
t de type triangle grâce à la fonction println. On verra dans les exercices qu’on peut définir des types
structurés qui utilisent d’autres types structurés, des tableaux de types structurés, etc.
On peut noter que nous sommes ici dans le formalisme objet, qui sous-tend la mise en oeuvre du
langage Java qui est derrière Javascool. Néanmoins nous n’irons pas au-delà, les notions avancées comme
celle-ci étant plutôt abordées en général dans les cursus universitaires à partir de la 2ème ou 3ème année.
import [Link].*;
void main ()
{
try {
PrintWriter fichier = new PrintWriter(new FileWriter("[Link]"), false);
[Link]("Hello");
[Link](1234);
[Link]();
}
catch(Exception e) {}
}
14. [Link]
15. [Link]
16. [Link]
44
Ce programme permet de sauvegarder, dans un fichier appelé [Link], plusieurs lignes de texte.
Après l’exécution (qui n’affiche rien à l’écran), ce fichier qu’on peut visualiser avec un logiciel de type
Wordpad ou Notepad contient :
Hello
1234
C’est donc un fichier ne contenant que du texte. Plusieurs lignes du programme méritent des explications :
– la ligne import [Link].*; indique au compilateur qu’on souhaite utiliser les fonctions spécifiques
pour la gestion des fichiers en Java
– la ligne PrintWriter fichier = new PrintWriter(..., false); sert à “ouvrir” le fichier souhaité,
avec en spécifiant avec false que si ce fichier existe déjà les données qu’il contenait seront perdues.
Au contraire, si ce booléen a la valeur true cela indique que, si le fichier existe déjà, les données
sauvegardées s’ajouteront automatiquement après.
– grâce à println on écrit des lignes de texte dans le fichier, comme on utilisait déjà cette fonction
pour afficher du texte à l’écran
– la fonction close qui “ferme” le fichier n’a pas une grande utilité dans cet exemple. Elle est par
contre obligatoire si on souhaite, plus tard pendant l’exécution du programme, ré-ouvrir le fichier.
– on observe l’apparition d’un bloc composé des lignes try { et catch(Exception e) {}, qui sans
rentrer dans les détails servent à traiter les cas d’erreurs éventuelles. Ce bloc est obligatoire et doit
entourer toutes les opérations sur les fichiers.
Les erreurs pouvant se produire concernent par exemple le nom choisi pour le fichier et le dossier où il
sera sauvegardé sur le disque dur. Dans notre exemple, le nom fourni est donné sans indication de dossier :
sous Linux il sera sauvegardé à la racine du compte de l’utilisateur, en général /home/utilisateur, sous
Windows ce sera plutôt C:\Users\utilisateur\Desktop\
On peut aussi spécifier le dossier, par exemple le dossier /tmp sous Linux :
PrintWriter fichier = new PrintWriter(new FileWriter("/tmp/[Link]"), false);
Sous Windows on utilisera plutôt "C:/[Link]", ou bien "F:/[Link]" pour sauveg-
arder sur une clef USB, etc.
Le problème ensuite est de savoir comment lire des données à l’intérieur d’un fichier texte. Deux cas
de figure se présentent selon qu’on connaı̂t exactement le contenu du fichier, ou bien qu’il existe des
inconnues, par exemple le nombre de lignes dans le fichier. Si le nombre de lignes est connu, on peut
écrire un programme de ce type :
try {
BufferedReader fichier2 = new BufferedReader(new FileReader("[Link]"));
String ligne;
ligne = [Link]();
println(ligne);
ligne = [Link]();
println(ligne);
[Link]();
}
catch(Exception e) {}
Ceci va afficher à l’écran les deux lignes de [Link]. La fonction readLine lit les données ligne
par ligne, ce qui implique de bien séparer les données ligne par ligne lors de la sauvegarde.
Si le nombre de lignes est inconnu, on aura plutôt :
BufferedReader fichier2 = new BufferedReader(new FileReader("[Link]"));
String ligne;
ligne = [Link]();
while(ligne != null) {
println(ligne);
ligne = [Link]();
}
[Link]();
45
Ici on comprend que la fonction readLine renvoie la valeur null s’il n’y a plus de données à lire dans le
fichier : ainsi on peut utiliser une boucle qui lit chaque ligne, affiche cette ligne à l’écran puis tente de
lire une nouvelle ligne.
46
println("a = " +a);
}
-- Affichage de la sortie --------
a = 1
a = 1
Ici la fonction f prend en paramètre un entier dont elle modifie la valeur, mais cette modification n’en-
traı̂ne pas d’effet de bord puisqu’on voit que la variable a, dans le programme principal, conserve sa
valeur initiale. Ceci s’explique par le fait que, quand la fonction f commence son exécution, le contenu
de la variable a est d’abord dupliqué dans une nouvelle variable x. Ce comportement est donc logique,
et vaut pour les types de base : int, double et String.
Pourtant, nous avons déjà vu que le comportement n’est pas le même lorsqu’on manipule des tableaux
(voir section 5.1) :
void f(int tab[]) {
tab[0] = 17;
}
void affichage(int tab[]) {
for (int i = 0; i < [Link]; i++)
print(tab[i]+" ");
println("");
}
void main ()
{
int a[] = {12, 7, 89, 32};
affichage(a);
f(a);
affichage(a);
}
-- Affichage de la sortie --------
12 7 89 32
17 7 89 32
On voit ici que la modification amenée par la fonction f est permanente : lorsqu’on affiche de nouveau
le tableau à l’intérieur de la fonction main, la modification est visible. Même si cela donne plus de
souplesse au programmeur, il faut être très vigilant pour déterminer si l’appel à une fonction ne risque
pas de modifier des données de façon inconsidérée. Ceci vaut aussi pour une variable de type structuré,
comme dans l’exemple suivant.
class triangle {
double cote1, cote2, cote3;
public String toString() {
return ("Triangle de cotés "+cote1+", "
+cote2+", "+cote3);
}
}
void f(triangle x) {
x.cote1 = 2;
}
void main ()
{
triangle a = new triangle();
a.cote1 = 10; a.cote2 = 15; a.cote3 = 7;
println(a);
f(a);
println(a);
}
47
-- Affichage de la sortie --------
Triangle de cotés 10.0, 15.0, 7.0
Triangle de cotés 2.0, 15.0, 7.0
Là encore, la modification apportée dans la fonction f est permanente et irréversible, donc méfiance !
Le même type de comportement peut être observé avec l’emploi de variables globales, c’est-à-dire des
variables “partagées” par l’ensemble du programme :
int a;
void f() {
a = 2;
}
void main () {
a = 1;
println("a = " +a);
f();
println("a = " +a);
}
-- Affichage de la sortie --------
a = 1
a = 2
Ici la variable a est déclarée en dehors de toute fonction, au début du programme. Par conséquent toute
fonction peut afficher ou modifier sa valeur, ce que fait la fonction f. Cette fois-ci, à la différence du
premier exemple montré ci-dessus, on voit que la modification est bien prise en compte.
Voici un programme, qui comme vous pourrez le constater en faisant un copier-coller dans Javascool, ne
fonctionne pas très bien... Comment le modifier ? Quels sont les effets de bord à l’oeuvre ici ?
// Inspiré par [Link]
int MAX_LIGNES = 10;
int Cpt_Lignes, // indique la ligne en train d’^ etre dessinée
Max_Car, // indique le max. de caractères sur la ligne
Cpt_Car; // indique le caractère en train d’^
etre dessiné
void Dessiner_Ligne () {
Max_Car = Cpt_Lignes;
Cpt_Lignes = 1;
while (Cpt_Car <= Max_Car) {
print("*");
Cpt_Car++;
}
Cpt_Lignes++;
}
void main () {
Cpt_Lignes = 1;
while (Cpt_Lignes <= MAX_LIGNES) {
48
Dessiner_Ligne ();
println("");
Cpt_Lignes++;
}
}
Quel doivent être les valeurs affichées ici ? Même question si l’on remplace le symbole && par || ? En
déduire le comportement de ces opérateurs si le premier test a la valeur false
49
L’activité “tortueLogo” définit des fonctions très simples permettant de dessiner à l’aide d’une petite
tortue qui peut se déplacer dans un écran de taille 512x512, munie d’un stylo qu’elle peut abaisser ou
relever. Avec les fonctions utilisables 17 , on peut par exemple dessiner une magnifique spirale comme sur
la figure 3 :
// Inspiré de
// [Link]
void spirale (double taille) {
if (taille < 40) {
forward(taille);
rightward(15);
spirale(taille*1.02);
}
}
void main() {
clear_all();
set_background(9);
set_color(0);
pen_up();
set_position(256,256);
pen_down();
spirale(10);
}
Le fait de pouvoir combiner des primitives de dessin très simples avec la puissance d’expression du langage
Java permet souvent d’illustrer de manière plus motivante l’application réelle de l’algorithmique, comme
dans l’exemple de la spirale qui utilise la récursivité. Nous reviendrons sur cet aspect dans les exercices.
On voit aussi dans l’exemple de l’onglet “Documents de la proglet” que la tortue Logo peut être utilisée
comme un classique grapheur de fonctions mathématiques.
L’activité “codagePixels” permet de manipuler des images et leur appliquer différents algorithmes,
mais également de dessiner à l’écran. La tortue Logo pouvait se déplacer dans un espace de taille fixe, ici
on pourra définir ses propres dimensions (inférieures à 500x500). L’onglet “Aide de la proglet” montre
les différentes opérations disponibles, que nous allons utiliser tout d’abord pour du dessin :
void main() {
int nb = 1500;
reset(256, 256);
int w = getWidth();
int h = getHeight();
int[] x = new int[nb];
int[] y = new int[nb];
int i;
for (i = 0; i < nb; i ++) {
x[i] = random( - w, w);
y[i] = random( - h, h);
}
while (true) {
for (i = 0; i < nb; i ++) {
x[i] += random( - 3, 3);
y[i] += random( - 3, 3);
if ( ! setPixel(x[i], y[i], 0)) {
x[i] = random( - w, w);
y[i] = random( - h, h);
}
}
17. [Link]
50
Figure 3 – Spirale obtenue avec la proglet “Logo”
51
sleep(100);
reset(256, 256);
}
}
Si vous exécutez cet exemple vous verrez 1500 petits points qui suivent une sorte de mouvement brownien.
La zone de dessin est fixée à la taille 512x512 puisqu’ici hauteur et largeur ne correspondent qu’à la moitié.
Les deux tableaux utilisés pour stocker les points sont initialisés puis dans une boucle infinie, déplacés
de façon aléatoire. On laisse les points s’afficher pendant 100 millisecondes, puis le dessin est effacé avant
de recommencer. La fonction setPixel qui permet de dessiner un point renvoie un booléen indiquant si
on est sorti des limites de l’écran, ce qui permet éventuellement de redonner une position aléatoire au
point.
Les notions présentées en bas de page de cette même activité montrent la possibilité de pouvoir
charger n’importe quelle image trouvée sur le web, au format JPG et de taille inférieure à 512x512, et
plutôt en niveaux de gris (ie pas d’images couleur). On peut ensuite appliquer des opérations à cette
image en la considérant comme une matrice 2D : en effet les éléments de l’image, appelés pixels, sont en
fait les cases d’une matrice dont les valeurs sont des intensités de gris, allant de 0 (noir) à 255 (blanc). Les
explications données dans ce tutoriel permettent de comprendre comment on peut récupérer le niveau
de gris pour un pixel donné avec getPixel, modifier cette valeur puis la ré-injecter dans l’image avec
setPixel. Cette approche permet d’implémenter toutes sortes de filtres (inversion vidéo, flou, emboss).
Voici un autre exemple permettant d’appliquer une symétrie à l’image verticale à une image (les pixels
de gauche passent à droite et inversement).
void main() {
load("[Link]
int w = getWidth(), h = getHeight();
int i, j;
sleep(1000);
// On attend un peu avant d’appliquer la symétrie
for (i = - w; i < 0; i ++) {
for (j = - h; j < h; j ++) {
int val1 = getPixel(i, j);
int val2 = getPixel( - i, j);
setPixel(i, j, val2);
setPixel( - i, j, val1);
}
}
}
Cette approche peut parfois se révéler un peu limitée, car toute modification sur l’image est immédiate.
Pour certaines opérations on devra utiliser une matrice d’entiers de taille wxh pour stocker les résultats
de façon temporaire.
52
Figure 4 – Exemple de liste chaı̂née d’entiers
fonctions de tracé, de remplissage et de “gommage”. Les plus audacieux pourront essayer, pour le tracé
d’une ligne, d’appliquer l’algorithme de Bresenham 19 .
53
Figure 5 – Exemple d’arbre binaire
54
Arbre a = new Arbre();
[Link] = v;
[Link] = g;
[Link] = d;
return a;
}
Arbre creerFeuille(int v) {
return creerArbre(v, null, null);
}
void main() {
Arbre a =
creerArbre(1,
creerArbre(2, creerFeuille(4),
creerArbre(5, creerFeuille(7),
creerFeuille(8))),
creerArbre(3, null,
creerArbre(6, creerFeuille(9),
null)));
println(a);
}
-- Affichage de la sortie --------
[ 1 [ 2 [ 4 ] [ 5 [ 7 ] [ 8 ] ] ] [ 3 [ 6 [ 9 ] ] ] ]
Ici la structure Arbre est munie de l’opération toString utilisée pour l’affichage, et qui implémente le
parcours préfixe de l’arbre : on affiche d’abord la racine, puis le sous-arbre gauche, puis le sous-arbre
droit. Encore une fois le mot-clef null symbolise le fait qu’un noeud peut ne pas avoir de fils à gauche
ou à droite.
55
void main() {
ArrayList<Integer> maListe = new ArrayList<Integer>();
for (int i = 0; i < 10; i++) {
[Link](random(15,20));
}
println(maListe);
println("Valeur stockée en position 0: "+ [Link](0));
println("1re apparition de la valeur 17 :"+ [Link](17));
println("Derniere apparition de 17 :"+ [Link](17));
[Link]();
}
-- Affichage de la sortie --------
[19, 20, 16, 19, 17, 19, 16, 15, 20, 17]
Valeur stockée en position 0: 19
1re apparition de la valeur 17 :4
Derniere apparition de 17 :9
Ici la taille de la liste n’est pas fixée initialement, néanmoins on peut lui ajouter ensuite 10 valeurs (tirées
au hasard entre 15 et 20). On voit que l’on peut facilement :
– ajouter une valeur (add) qui doit être du type spécifié, qui s’ajoute à la fin de la liste
– afficher le contenu de la liste directement grâce à println
– savoir quelle valeur est stockée à une position donnée (get) : ici on s’intéresse à la position 0, mais
il faut comme avec les tableaux faire attention à ne pas essayer d’accéder à une position qui n’existe
pas
– savoir à quelle position apparaı̂t une valeur donnée, soit sa première apparition (indexOf ) soit la
dernière (lastIndexOf )
– effacer le contenu de la liste (clear)
– d’autres instructions ne sont pas montrées ici (notamment size et remove)
La structure HashSet est un peu différente : elle permet de stocker un ensemble de valeurs sans
permettre les doublons. Elle est donc bien adaptée pour stocker des valeurs uniques, par exemple la table
périodique des éléments, les pays du monde, les mois de l’année, . . . L’exemple ci-dessous montre un
HashSet stockant des chaı̂nes de caractères :
import [Link];
void main() {
HashSet<String> monEnsemble = new HashSet<String>();
[Link]("Bonjour");
[Link]("Benoit");
[Link]("Ca va");
[Link]("Bonjour");
println(monEnsemble);
println("Contient Bonjour ? "+ [Link]("Bonjour"));
[Link]("Bonjour");
println(monEnsemble);
}
-- Affichage de la sortie --------
[Benoit, Bonjour, Ca va]
Contient Bonjour ? true
[Benoit, Ca va]
– la ligne import ... indique au compilateur qu’on souhaite utiliser les fonctions spécifiques pour
HashSet
– là aussi on peut ajouter des valeurs avec add, et afficher directement l’ensemble des valeurs avec
println. On se rend compte que les doublons ne sont pas acceptés, et que l’ordre dans lequel les
valeurs sont stockées n’est pas celui dans lequel on a ajouté les valeurs
– contains permet de savoir si un élément donné existe dans l’ensemble, et remove permet de
supprimer un élément
56
Les structures ArrayList et HashSet acceptent une forme particulière de la boucle for qui permet
de parcourir les éléments contenus dans la liste ou l’ensemble. Le bout de code suivant affiche les éléments
contenus dans un HashSet¡String¿ :
for (String elt:monEnsemble) {
println("Elt: "+elt);
}
Enfin, la structure HashMap permet de stocker des couples d’éléments : le premier est appelé la
clef, le second la valeur. Les clefs doivent être uniques, et une seule valeur peut être associée à une clef.
Cette structure est adaptée par exemple pour stocker des notes d’élèves dans une matière donnée :
void main() {
HashMap<String,Double> maMap = new HashMap<String,Double>();
[Link]("Benoit",12.0);
[Link]("Hervé",17.5);
[Link]("Charles",15.0);
[Link]("Benoit",14.5);
println(maMap);
println("Note de Charles: "+ [Link]("Charles"));
[Link]("Hervé");
println(maMap);
}
-- Affichage de la sortie --------
{Benoit=14.5, Hervé=17.5, Charles=15.0}
Note de Charles: 15.0
{Benoit=14.5, Charles=15.0}
Cette fois-ci c’est l’instruction put qui permet d’ajouter des couples (une chaı̂ne de caractère pour le
nom de l’élève et un réel pour sa note), mais si l’on essaie d’associer plusieurs notes au même élève c’est
la dernière qui est conservée. On peut encore afficher le contenu par println, supprimer un couple par
remove, et l’instruction get permet d’accéder à la valeur associée à une clef : ici la note associée à un
élève.
57
La structure Stack est une implémentation des piles vues en algorithmique, dotées des fonctions
push (ajouter un élément au sommet de la pile) et pop (enlever l’élément présent au sommet de la
pile) :
import [Link];
void main() {
Stack<String> maPile = new Stack<String>();
[Link]("Benoit");
[Link]("Hervé");
[Link]("Charles");
println([Link]());
println(maPile);
println([Link]());
println(maPile);
[Link]();
[Link]();
[Link]();
}
-- Affichage de la sortie --------
Charles
[Benoit, Hervé, Charles]
Charles
[Benoit, Hervé]
[Link]
Ici la pile sert à stocker des chaı̂nes de caractères, et l’on voit comment la déclarer et l’afficher avec
println. On peut dépiler un élément avec pop ou simplement voir cet élément sans le supprimer avec
peek 26 . Attention bien sûr à ne pas trop dépiler, comme à la fin de ce programme qui génère un message
d’erreur.
De la même façon, la structure Queue propose une implémentation d’une file avec les opérations
add (ajouter un élément à la fin de la file) et remove (enlever l’élément présent en tête de file) :
import [Link].*;
void main() {
Queue<String> maFile = new LinkedList<String>();
[Link]("Benoit");
[Link]("Hervé");
[Link]("Charles");
println(maFile);
[Link]();
println(maFile);
}
-- Affichage de la sortie --------
[Benoit, Hervé, Charles]
[Hervé, Charles]
Sans rentrer dans les détails, on voit qu’on utilise en fait une structure appelée LinkedList mais dans
la pratique cela n’a pas d’importance. Une fonction peek est également disponible pour voir l’élément
en tête de file sans le supprimer.
Pour ces deux structures, on peut connaı̂tre le nombre d’éléments stockés grâce à la fonction size(),
ce qui permet notamment d’éviter d’employer les opérations de suppression du premier élément si ce
nombre est nul.
58
Figure 6 – Graphe et sa représentation par matrice d’adjacence
sion est correctement parenthésée, en interdisant par exemple l’expression “)(a + b)/(d” ?
7.4 Graphes
On pourrait représenter un graphe avec un chaı̂nage similaire à ceux vus à la section 7.1. On représente
parfois aussi les graphes par une matrice d’adjacence, comme sur la figure 6. Il est évidemment possible
d’implémenter de telles structures et des opérations pour les manipuler, par exemple pour calculer le
plus court chemin d’un point à un autre d’un graphe 27 .
Dans cette section nous préférons nous intéresser aux activités déjà présentes dans Javascool à pro-
pos des graphes, qui permettent de ne pas avoir à recoder ces structures et de s’intéresser plutôt aux
algorithmes que l’on peut leur appliquer.
L’activité à choisir au lancement de Javascool s’appelle “gogleMaps”, c’est la plus simple à utiliser.
L’onglet “Aide de la proglet” explique d’abord comment afficher des positions et des chemins. Par exemple
avec le code ci-dessous on obtient deux positions reliées par un segment sur la carte accessible par l’onglet
“googlemap” :
void main()
{
effaceCarte();
affichePointSurCarte(2,47);
affichePointSurCarte(4,46);
afficheRouteSurCarte(2,47,4,46);
}
Les données géographiques (longitudes et latitudes des villes françaises et relations de voisinage entre
elles) sont stockées dans des structures similaires à celles vues à la section 7.2. Par exemple, le code
ci-dessous affiche sur la carte toutes les villes déjà enregistrées :
27. [Link]
59
int i = 1;
for (String ville:[Link]()) {
double longitude = [Link](ville);
double latitude = [Link](ville);
affichePointSurCarte(longitude, latitude, i++);
println(ville + "/" + longitude + "/" + latitude);
}
-- Affichage de la sortie --------
Troyes/4.052795/48.287473
Lens/3.056121/50.381367
Nantes/-1.564819/47.240526
...
Il est aussi possible de tracer des segments de droite entre deux positions, et également d’accéder à la
liste des villes voisines d’une ville donnée :
Le stockage des relations de voisinage définit de façon implicite un graphe non-orienté (pour toute relation
de voisinage d’une ville A vers une ville B, il existe également une relation de voisinage de B vers A). Une
fonction spécifique permet de déterminer le plus court chemin de A vers B, en prenant comme valuation
des arêtes la distance euclidienne entre les villes. Cette fonction renvoie une liste de toutes les villes à
traverser :
60
Figure 7 – Boı̂te de dialogue simple
61
Figure 8 – Boı̂te de confirmation
On peut également utiliser une boı̂te pour permettre la saisie d’un texte, comme sur la figure 9. La
variable reponse contient le texte tapé, ou bien null si l’utilisateur a fermé la boı̂te sans répondre :
62
Figure 10 – Saisie à partir de choix prédéfinis
import [Link].*;
import [Link].*;
JButton bouton1;
JFrame panneau;
class GestionClic implements ActionListener {
public void actionPerformed(ActionEvent e) {
[Link]("Arg, je suis cliqué");
}
}
void main() {
panneau = new JFrame("Panneau");
bouton1 = new JButton("Du texte sur le bouton");
[Link](new GestionClic());
[Link](bouton1);
[Link](300,100);
[Link](true);
}
Dans cet exemple, on définit comme variables globales le bouton et le panneau, puis une structure
GestionClic comportant une fonction actionPerformed. C’est cette fonction qui implémente ce qui
doit se passer lors d’un clic (ici, on modifie simplement le texte du bouton). Dans le programme main, on
crée les variables puis on associe, à l’aide de l’opération addActionListener, la structure GestionClic
63
Figure 12 – Un panneau de contrôle avec deux boutons
au bouton. On ajoute le bouton au panneau, on lui donne une taille, puis (enfin) le panneau s’affiche à
l’écran comme sur la figure 11. Si vous exécutez ce code vous aurez peut-être l’impression que seul un
clic est pris en compte : c’est normal puisque le texte est changé une première fois, ensuite c’est le même
texte qui est ré-affiché à chaque fois (mais tous les clics sont correctement gérés).
Notre second exemple propose maintenant deux boutons, pour obtenir un panneau tel que celui de
la figure 12 :
import [Link].*;
import [Link].*;
import [Link].*;
JButton bouton1, bouton2;
JFrame panneau;
class GestionClic implements ActionListener {
public void actionPerformed(ActionEvent e) {
if ([Link]() == bouton1) {
[Link]("Arg, je suis cliqué");
}
else {
[Link]("Arg, je suis cliqué");
}
}
}
void main() {
panneau = new JFrame("Panneau");
bouton1 = new JButton("Un bouton");
bouton2 = new JButton("Un autre bouton");
GestionClic gc = new GestionClic();
[Link](gc);
[Link](gc);
[Link]( new FlowLayout() );
[Link](bouton1);
[Link](bouton2);
[Link](300,100);
[Link](true);
}
Les différences avec le premier exemple sont d’abord la déclaration de deux variables de type JButton,
puis dans le code de la structure GestionClic on voit qu’un traitement permet de différencier si le clic a
été déclenché sur le bouton 1 ou le bouton 2. Dans la partie main on voit que l’association entre boutons
et structure GestionClic change un peu (on commence par créer une variable de type GestionClic,
puis on l’associe aux deux boutons). Enfin, on voit apparaı̂tre une instruction setLayout, qui indique
que les boutons sont placés horizontalement les uns à côté des autres.
Le dernier exemple, un peu plus évolué, montre une combinaison de la tortue LOGO (section 6.4)
avec un panneau de contrôle permettant de bouger la tortue “à la main”, comme sur la figure 13. Il faut
64
Figure 13 – Un panneau de contrôle avec cinq boutons
d’abord ouvrir l’activité “Programmer avec la tortue Logo” pour pouvoir exécuter ce code, qui utilise un
autre type de layout permettant de placer les boutons dans 5 zones définies par les points cardinaux :
import [Link].*;
import [Link].*;
import [Link].*;
JButton avancer, gauche, droite, tracer, lever;
JFrame panneau;
class GestionClic implements ActionListener {
public void actionPerformed(ActionEvent e) {
Object bouton = [Link]();
if (bouton == avancer) {forward(10);}
else if (bouton == gauche) {leftward(10);}
else if (bouton == droite) {rightward(10);}
else if (bouton == tracer) {pen_down();}
else {pen_up();}
}
}
void main() {
clear_all();
set_background(9);
set_color(0);
pen_up();
set_position(256,256);
panneau = new JFrame("Panneau");
avancer = new JButton("Avancer");
gauche = new JButton("A gauche");
droite = new JButton("A droite");
tracer = new JButton("Tracer");
lever = new JButton("Lever le stylo");
GestionClic gc = new GestionClic();
[Link](gc);
[Link](gc);
[Link](gc);
[Link](gc);
[Link](gc);
[Link](new BorderLayout());
[Link](tracer, [Link]);
[Link](lever, [Link]);
[Link](droite, [Link]);
65
[Link](gauche, [Link]);
[Link](avancer, [Link]);
[Link](300,300);
[Link](true);
}
9.1.2 Variables
Exercice du tutoriel sur les variables, un programme un peu amélioré :
void main() {
println("Bonjour, quel est ton nom ?");
String nom = readString();
println("Et quel est ton ^age ?");
int age = readInteger();
println("Enchanté " + nom + ", " +
age + " ans est un bel ^age !");
println("Mais quelle est ta moyenne ?");
double moy = readDouble();
println(moy + " de moyenne c’est pas mal.");
}
Exercice 3.2.3 :
void main() {
double rayon, circ, aire;
// le mot-clef final sert à indiquer une constante
final double Pi = 3.14159;
println("Entrer le rayon: ");
rayon = readDouble();
circ = 2 * rayon * Pi;
aire = Pi * rayon * rayon;
println("Circonférence du cercle de rayon " +
rayon + ": " + circ);
66
println("Aire: " + aire);
}
Exercice 3.2.4 :
void main() {
double rayon, circ, aire;
println("Entrer le rayon: ");
rayon = readDouble();
// Autre amélioration: il existe une constante prédéfinie PI
circ = 2 * rayon * PI;
aire = PI * pow(rayon, 2);
println("Circonférence du cercle de rayon " +
rayon + ": " + circ);
println("Aire: " + aire);
}
Exercice 3.2.5 :
void main() {
double a, b, c, d;
println("Entrer les coeffs d’une équation du 2nd degré (le déterminant doit ^
etre positif)");
println("Entrer a:");
a = readDouble();
println("Entrer b:");
b = readDouble();
println("Entrer c:");
c = readDouble();
d = b*b - (4*a*c);
println("Déterminant: " + d);
println("Racine x1 = " + (-b + sqrt(d))/(2*a));
println("Racine x2 = " + (-b - sqrt(d))/(2*a));
}
void main() {
println("Bonjour, quel est ton nom ?");
String nom = readString();
if((equal(nom, "Toto")) || (equal(nom, "Dieu"))) {
println("Ce n’est pas crédible !");
}
else if(equal(nom, "Nadia")) {
println("Super !");
}
println("Et quel est ton ^ age ?");
int age = readInteger();
if ((age >= 3) && (age <= 120)) {
println("OK !");
}
else {
println("Ce n’est pas sérieux !");
}
}
Exercice sur le cercle (version améliorée) :
67
void main() {
double rayon, circ, aire;
println("Entrer le rayon: ");
rayon = readDouble();
if (rayon < 0) {
println("Erreur, calcul impossible");
}
else {
circ = 2 * rayon * PI;
aire = PI * pow(rayon, 2);
println("Circonférence du cercle de rayon " +
rayon + ": " + circ);
println("Aire: " + aire);
if (circ < aire) {
println("La circonférence est inférieure à l’aire");
}
else if (circ > aire) {
println("La circonférence est supérieure à l’aire");
}
else {
println("La circonférence est égale à l’aire");
}
}
}
Exercice 3.3.3 :
void main() {
int a, b, c;
println("Entrer a, b et c: ");
a = readInteger();
b = readInteger();
c = readInteger();
if (a < b) {
if (b < c) {
println ("a < b < c");
}
else if (a < c) {
println ("a < c < b");
}
else {
println ("c < a < b");
}
}
else {
if (a < c) {
println ("b < a < c");
}
else if (b < c) {
println ("b < c < a");
}
else {
println ("c < b < a");
}
}
// On ne peut pas écrire: if (a < b < c) ...
// mais on peut combiner: if ((a < b) && (b < c)) ...
68
}
Exercice 3.3.4 :
void main() {
double a, b, c, d;
println("Entrer les coeffs d’une équation du 2nd degré (le déterminant doit ^
etre positif)");
println("Entrer a:");
a = readDouble();
println("Entrer b:");
b = readDouble();
println("Entrer c:");
c = readDouble();
d = b*b - (4*a*c);
println("Déterminant: " + d);
if (d < 0) {
println("Calcul des racines impossible");
}
else if (d > 0) {
println("Racine x1 = " + (-b + sqrt(d))/(2*a));
println("Racine x2 = " + (-b - sqrt(d))/(2*a));
}
else {
println("Racine double = " + (-b/(2*a)));
}
}
9.1.4 Fonctions
Exercices du tutoriel sur les fonctions :
// Exercice 1
int min (int x, int y) {
if (x < y) {
return x;
} else {
return y;
}
}
int max (int x, int y) {
if (x > y) {
return x;
} else {
return y;
}
}
int abs(int x) {
return (max(-x, x));
}
void main() {
int x = 12;
int y = -14;
println("Max = " + max(abs(x), abs(y)) +
" et Min = " + min(abs(x), abs(y)));
}
// Exercice 2a
69
double div(double x, double y) {
if (y == 0) {
return [Link];
} else {
return x/y;
}
}
void main() {
double a, b;
println ("Entrer a et b: ");
a = readDouble(); b = readDouble();
println ("a/b = " + div(a, b));
}
// Exercice 2b
boolean xor (boolean x, boolean y) {
if ((x) && (y)) {
// Ou: if ((x == true) && (y == true))
return false;
}
return true;
}
// Exercice 2c
int max (int x, int y) {
if (x > y) {
return x;
} else {
return y;
}
}
int max (int x, int y, int z) {
return max(x, max(y, z));
// Javascool comprend qu’on veut faire appel
// à la fonction max de 2 variables
}
void main() {
int a, b, c;
println("Entrer a, b et c: ");
a = readInteger();
b = readInteger();
c = readInteger();
println("Max = " + max(a, b, c));
}
// Exercice 2d
void infox(String question, boolean reponse) {
println(question + ": true ou false ?");
boolean choix = readBoolean();
if (choix == reponse) {
println("Bravo !");
} else {
println("Tu t’es gentiment trompé...");
}
}
void main() {
infox("Il y a environ 100 millions d’ordinateurs dans le monde", false);
70
infox("Ada Byron-Lovelace était, dès le XIXème siècle, la 1ère femme informaticienne", true);
infox("Toutes les fonctions mathématiques peuvent ^ etre calculées par un ordinateur", false);
infox("Le cours de la 2nd guerre mondiale a changé plus vite gr^ ace à ... un calcul informatique", tr
}
71
println("Racine x2 = " + (-c2 - sqrt(d))/(2*c1));
}
else {
println("Racine double = " + (-c2/(2*c1)));
}
}
void main() {
double a, b, c;
println("Entrer les coeffs d’une équation du 2nd degré");
println("Entrer a:");
a = readDouble();
println("Entrer b:");
b = readDouble();
println("Entrer c:");
c = readDouble();
calcul (a, b, c);
}
9.1.5 Boucles
Exercices du tutoriel :
// Huit affichages
void main() {
int n = 1;
while( n <= 8) {
println("Hello World !");
n = n + 1;
}
}
// Nombres impairs de 1 à 39
void main() {
int n = 1;
while( n <= 39) {
println("n = " + n);
n = n + 2;
}
}
// Approximation de Pi
double archimede (int n) {
// On démarre avec des polygones à 3 c^
otés
int i = 3;
// Périmètre du polygone inscrit
double pInscrit = 2 * sqrt(2);
72
// Périmètre du polygone circonscrit
double pCircons = 4;
// Moyenne des deux périmètres
double moy = (pInscrit + pCircons)/2;
while(i <= n) {
pCircons = pCircons * pInscrit/moy;
pInscrit = sqrt(pInscrit * pCircons);
moy = (pInscrit + pCircons)/2;
i++;
}
return (2*pInscrit + pCircons)/3;
}
void main() {
println("Entrez n: ");
int n = readInteger();
println("Approximation de Pi d’ordre " + n + ": " + archimede(n));
}
void main() {
double rayon, circ, aire;
println("Entrer le rayon: ");
rayon = readDouble();
while (rayon < 0) {
println("Erreur, rayon négatif. Entrer le rayon: ");
rayon = readDouble();
}
circ = 2 * rayon * PI;
aire = PI * rayon * rayon;
println("Circonference du cercle de rayon " +
rayon + ": " + circ);
println("Aire: " + aire);
}
void main() {
// Les variables doivent ^ etre initialisées pour
// que Javascool soit s^ur de pouvoir faire les calculs
double a = 1, b = 1, c = 1, d = -1;
while (d < 0) {
println("Entrer les coeffs: ");
println("Entrer a:");
a = readDouble();
println("Entrer b:");
b = readDouble();
println("Entrer c:");
c = readDouble();
d = b*b - (4*a*c);
if (d < 0) {
println("Erreur, le déterminant est négatif");
}
}
println("Déterminant: " + d);
println("Racine x1 = " + (-b + sqrt(d))/(2*a));
println("Racine x2 = " + (-b - sqrt(d))/(2*a));
}
73
Exercice sur l’affichage des étoiles :
void afficherEtoiles (int n) {
for (int i = 1; i <= n; i++) {
print("*");
}
println(""); // Retour à la ligne
}
void afficherLigneVide (int n) {
print("*");
for (int i = 1; i <= (n-2); i++) {
print(" ");
}
println("*");
}
void main() {
afficherEtoiles(17);
afficherLigneVide(17);
println("* Hello World ! *");
afficherLigneVide(17);
afficherEtoiles(17);
}
74
}
}
void main() {
fibo1();
fibo2();
fibo3();
}
75
}
cpt++;
}
if (gagne) {
println("J’ai trouvé " + x + " en " +
cpt + " coups !");
} else {
println("J’ai perdu...");
}
}
9.2 Section 4
9.2.1 Le jeu du calcul mental
Cette correction proposée par une enseignante de l’académie de Nice va plus loin que le sujet en
enregistrant le meilleur score dans un fichier : cet enregistrement est effectué à la fin du jeu, et servira
au prochain lancement du programme à vérifier si le meilleur score a été battu. Attention, le symbole /
dans ce jeu correspond à la division entière !
import [Link]. *;
void affichageDuree(long d) {
/* Affiche la durée en minute et seconde et milliseconde*/
int min, ms,s;
if (d < 1000) {
println((int)d + " millisecondes");
}
if (d > 1000 && d < 60000) {
s = (int)d / 1000;
ms = (int)d % 1000;
println(s + " secondes et " + ms + " millisecondes");
} else {
min = (int)d / 60000;
ms = (int)(d - 60000 * min) % 1000;
s = (int)(d - (min * 60000) - ms) / 1000;
println(min + " minutes et " + s + " secondes et " + ms + " millisecondes");
}
}
76
case 3 : resultatJ = a * b;
break;
case 4 : resultatJ = a / b;
break;
}
return resultatJ;
}
File demandePseudoPourEnregistrement() {
/*On demande le pseudo et chemin pour enregistrer le meilleur temps, ou avoir
* le chemin du fichier contenant le meilleur temps pour un ancien joueur
* Le fichier est créé si nécessaire
* Si le joueur est ancien, son meilleur temps est écrit sur la console
* On retourne le chemin du fichier : chemin/CalculMentalpseudo
*/
String pseudo = "",chemin;
File cheminFichier = null;
File cheminRepertoire = null;
Boolean fichierCreer = false,fichierExisteDeja;
while ([Link]("")) {
pseudo = readString("Quel est votre pseudo ? ");
}
while (fichierCreer == false) {
chemin = readString("Dans quel REPERTOIRE voulez vous sauvegardez votre
meilleur temps (ne pas oublier le slash à la fin) : ?");
cheminRepertoire = new File(chemin);
77
while ( ! [Link]()) {
println("mauvais chemin");
cheminRepertoire = new File(chemin);
chemin = readString("MAUVAIS CHEMIN, dans quel repertoire voulez vous
sauvegardez votre meilleur temps ?");
}
try {
cheminFichier = new File([Link]("calculMental").concat(pseudo));
println("Le fichier sera enregistrer à cet endroit : " + cheminFichier);
fichierExisteDeja = [Link]();
//Si le fichier existe deja, on indique que le joueur est ancien,
// et on affiche le meilleur temps déjà enregistré
if (fichierExisteDeja == false) {
println("Vous avez déjà joué !!");
println("Votre meilleur score enregistré est :");
affichageDuree(lireMeilleurTemps(cheminFichier));
println("");
}
fichierCreer = true;
}
catch(Exception e) {
println("le fichier ne peut pas ^ etre crée, choississez un autre répertoire");
}
}
return cheminFichier;
}
78
Long lireMeilleurTemps(File cheminFichier) {
/*retourne le meilleur temps enregistré dans le fichier
Si il y a un problème de lecture (c’est à dire aussi si le fichier est vide
pur un nouveau joueur par exemple), renvoie la plus grande valeur possible
pour un long*/
try{
BufferedReader fichier4 = new BufferedReader(new FileReader(cheminFichier));
String ligne = [Link]();
[Link]();
return [Link](ligne);
}
catch(Exception e3) {
return Long.MAX_VALUE;
}
}
void main() {
int operation, a,b;
String recommence = "O";
boolean VF;
String enregistre;
/* "O" si on veut enregistrer dans un fichier le meilleur temps*/
int resultat,resultatJuste;
File cheminFichier = null;
/* chemin du fichier de l’enregistrement*/
long meilleurTemps = Long.MAX_VALUE;
println("Bonjour, et bienvenue dans le jeu du calcul mental");
//demande si on veut enregistrer le meilleur score
do{
enregistre = readString("Voulez vous enregistrer votre score ?(O/N)");
}
while ( ! ([Link]("O") || [Link]("N")));
if ([Link]("O")) {
/* si on souhaite enregistrer on crée le fichier ou bien si le joueur
* est ancien, on enregistre le chemin du fichier d’enregistrement et on lit
* le meilleur temps obtenu
*/
cheminFichier = demandePseudoPourEnregistrement();
meilleurTemps = lireMeilleurTemps(cheminFichier);
}
while ([Link]("O") || [Link]("o")) {
/* determination des 2 valeurs aléatoires*/
a = random(1, 11);
b = random(1, 11);
/* choix aléatoire de l’opération à effectuer */
operation = random(1, 5);
/*demande la réponse, et calcul la durée mise pour répondre correctement*/
println(phraseOperation(operation, a, b));
VF = false;
resultatJuste = resultatJuste(operation, a, b);
long debut = [Link]();
while (VF == false) {
resultat = readInteger(phraseOperation(operation, a, b));
VF = vraiFaux(resultat, resultatJuste);
}
79
long fin = [Link]();
/*affiche la durée sous forme s+ms ou min+s+ms*/
affichageDuree(fin - debut);
if (meilleurTemps > (fin - debut)) {
meilleurTemps = fin - debut;
println("BRAVO !!!!!!!");
}
else println("Vous n’avez pas battu votre meilleur temps !");
recommence = readString("Voulez vous recommencez ? O ou N");
}
if ([Link]("O")) {
enregistrementMeilleurTemps(meilleurTemps, cheminFichier);
}
}
9.3 Section 8
9.3.1 Le jeu du pendu
Cette correction proposée par Pierre Toujas montre l’utilisation des boı̂tes de dialogue pour saisir les
différents éléments du jeu (nom des joueurs, puis chaque lettre). Vous pouvez l’utiliser avec vos propres
mots, et aussi continuer à l’enrichir pour proposer par exemple un affichage du vrai “pendu”, comme
quand vous jouez à ce jeu sur une feuille de papier !
/* Programme créé par Rodolphe DUPUIS, Lisa LEVASSEUR et Pierre TOUJAS. */
/* Contact: [Link]@[Link] */
import [Link]. *;
void main(){
[Link](getPane(), "Bienvenue ! \nBonne partie sur
\"Le Pendu Scientifique\" !" );
80
"molecule", "nombre", "numerique", "onde", "particule", "periode",
"probabilite", "quadrilatere", "quotient", "racine", "rayon",
"segment","signal","suite", "tangente", "univers", "vecteur",
};
JOptionPane jop = new JOptionPane(), jop2 = new JOptionPane();
String mot = (String)[Link](null, J1+", choisissez un mot dans
cette liste.\n"+J2+", ne regardez pas ...",
"Choix du mot", JOptionPane.QUESTION_MESSAGE, null, mots, mots[0]);
[Link](null, "Vous avez choisi le mot \"" + mot +"\"",
"Choix du mot", JOptionPane.INFORMATION_MESSAGE);
81
10 Annexe : Aide-mémoire Javascool
82
Syntaxe générale du langage Javascool / Java
3. Types
int, double, boolean, char, String
Rq : Les caractères sont encadrés par des simples quotes : ‘o’
Les chaînes sont délimitées par des doubles quotes : "chaîne"
4. Commentaires
// Exemple de commentaire
5. Séquence
{
Action_1
Action_2
…
}
6. Action de lecture/écriture
variable = readInt(); (ou readDouble(), readString(), etc.)
println(liste des éléments à afficher);
7. Affectation
variable = expression;
8. Action conditionnelle
if (condition) {
actions_si
} else {
actions_sinon
}
9. Choix multiple
switch (variable) {
case valeur_1: actions_1
break;
case valeur_2: actions_2
break;
…
default: actions_sinon
}
while (condition) {
actions
}
do {
actions
} while (condition)
11. Sous-programmes
Syntaxe d'une procédure :
void nom_procédure() void nom_procédure(paramètres)
{ {
// Déclaration des variables ou // Déclaration des variables
// Actions // Actions
} }
Appel d'une procédure :
nom_procédure() ou nom_procédure(variables ou constantes)
Les paramètres sont séparés par des virgules et spécifiés sous la forme : type nom_paramètre
Les paramètres appartenant à un type de base (int, double, boolean, char)sont forcément des paramètres d'entrée, les autres
sont des paramètres d'Entrée/Sortie
Syntaxe d'une fonction (les paramètres fonctionnent de manière similaire) :
type_retour nom_fonction() type_retour nom_fonction(paramètres)
{ {
// Déclaration des variables ou // Déclaration des variables
// Actions // Actions
} }
Pour « retourner » une valeur, on place dans le corps de la fonction une ou plusieurs actions de type :
return(valeur)
Appel d'une fonction :
Variable = nom_fonction() ou variable = nom_fonction(variables ou constantes)
12. Fichiers
Manipulation par des variables de ou type PrintWriter (écriture) ou BufferedReader (lecture)
Création d'un nouveau fichier (pour écriture) :
PrintWriter fichier = new PrintWriter(new FileWriter("[Link]"), false);
(si le fichier existait déjà les données sont perdues)
Ajout de données dans un fichier existant (pour écriture) :
PrintWriter fichier = new PrintWriter(new FileWriter("[Link]"), true);
Désignation d'un fichier existant (pour lecture) :
BufferedReader fichier2 = new BufferedReader(new FileReader("[Link]"));
Fermeture : [Link]();
Lecture : String ligne = [Link]();
Cette fonction renvoie null s'il n'y a plus de lignes à lire dans le fichier.
Ecriture : [Link](données);
13. Structures
class nom_structure
{
…
}
Déclaration de variable : nom_structure variable = new nom_structure();
Accès à un élément d'une variable de type structure : variable.élément
16. Pointeurs
En Javascool toute variable n'appartenant pas à un type de base (int, double, boolean, char)est forcément un pointeur.
Attention en modifiant les champs d'une variable de type structure...