 |
 |
3) Contrôle du flux du programme |
|
 |
|
Texte original |
 |
Traducteur : Jean-Pierre VIDAL |
|
 |
///
|
Ce chapitre contient 6 pages
1
2
3
4
5
6
|
|
|
 |
 |
 |
 |
 |
 |
|
 |
|
 |
 |
 |
Integral-selector is an expression
that produces an integral value. The switch compares the result of
integral-selector to each integral-value. IF it finds a match, the
corresponding statement (simple or compound) executes. If no match
occurs, the default statement
executes.
|
 |
sélecteur-entier est une expression qui produit une valeur
entière. switch compare le résultat de sélecteur-entier avec chaque
valeur-entière. S'il trouve une égalité, l'instruction correspondante (simple ou
composée) est exécutée. Si aucune égalité n'est trouvée, l'instruction qui suit
default est exécutée.
|
 |
 |
 |
You will notice in the above definition
that each case ends with a break, which
causes execution to jump to the end of the switch body. This is the
conventional way to build a switch statement, but the break is
optional. If it is missing, the code for the following case statements execute
until a break is encountered. Although you don’t usually want this
kind of behavior, it can be useful to an experienced programmer. Note the last
statement, following the default, doesn’t have a break
because the execution just falls through to where the break would have
taken it anyway. You could put a break at the end of the default
statement with no harm if you considered it important for style’s
sake.
|
 |
Notons dans la définition précédente que chaque instruction
case se termine par une instruction break, qui provoque un saut à
la fin du corps de l'instruction switch. Ceci est la manière habituelle de
construire une instruction switch. Cependant break est optionnel.
S'il n'est pas là, le code associé à l'instruction case suivante est exécuté, et
ainsi de suite jusqu'à la rencontre d'une instruction break. Bien qu'on n'ait
généralement pas besoin d'utiliser cette possibilité, elle peut se montrer très utile pour un
programmeur expérimenté. La dernière instruction, qui suit default, n'a pas besoin
de break car en fait l'exécution continue juste à l'endroit où une instruction
break l'aurait amenée de toute façon. Il n'y a cependant aucun problème à terminer
l'instruction default par une instruction break si on pense que
le style de programmation est une question importante.
|
 |
 |
 |
The switch statement is a clean
way to implement multi-way selection (i.e., selecting from among a number of
different execution paths), but it requires a selector that evaluates to an
integral value such as int or char. If you want to use, for
example, a string or a floating-point number as a selector, it won’t work
in a switch statement. For non-integral types, you must use a series of
if statements.
|
 |
L'instruction switch est une manière propre d'implémenter
une sélection multiple (c'est à dire sélectionner un certain nombre de possibilités d'exécution),
mais elle requiert une expression de sélection dont le résultat soit entier, comme
int ou char. Par exemple on ne peut pas utiliser une chaîne de
caractères ou un flottant comme sélecteur dans une instruction switch. Pour les
types non entiers, il faut mettre en oeuvre une série d'instructions
if.
|
 |
 |
 |
Here’s an example that creates
letters randomly and determines whether they’re vowels or
consonants:
|
 |
Voici un exemple qui crée des lettres aléatoirement, et détermine s'il
s'agit d'une voyelle ou d'une consonne :
|
 |
 |
 |
//: c03:VowelsAndConsonants.java // Demonstrates the switch statement.
public class VowelsAndConsonants { public static void main(String[] args) { for(int i = 0; i < 100; i++) { char c = (char)(Math.random() * 26 + 'a'); System.out.print(c + ": "); switch(c) { case 'a': case 'e': case 'i': case 'o': case 'u': System.out.println("vowel"); break; case 'y': case 'w': System.out.println( "Sometimes a vowel"); break; default: System.out.println("consonant"); } } } } ///:~
|
 |
//: c03:VowelsAndConsonants.java // Démonstration de l'instruction switch.
public class VowelsAndConsonants { public static void main(String[] args) { for(int i = 0; i < 100; i++) { char c = (char)(Math.random() * 26 + 'a'); System.out.print(c + ": "); switch(c) { case 'a': case 'e': case 'i': case 'o': case 'u': System.out.println("vowel"); break; case 'y': case 'w': System.out.println( "Sometimes a vowel"); break; default: System.out.println("consonant"); } } } } ///:~
|
 |
 |
 |
Since Math.random( )
generates a value between 0 and 1, you need only multiply it by the upper bound
of the range of numbers you want to produce (26 for the letters in the alphabet)
and add an offset to establish the lower bound.
|
 |
Math.random( ) générant une valeur entre 0 et 1, il
faut la multiplier par la borne supérieure de l'intervalle des nombres qu'on veut produire (26 pour
les lettres de l'alphabet) puis ajouter un décalage correspondant à la borne inférieure.
|
 |
 |
 |
Although it appears you’re
switching on a character here, the switch statement is actually using the
integral value of the character. The singly-quoted characters in the case
statements also produce integral values that are used for
comparison.
|
 |
Bien qu'il semble que la sélection s'opère ici sur un type caractère,
l'instruction switch utilise en réalité la valeur entière du caractère. Les
caractères entre guillemets simples des instructions case sont également
interprétés comme des entiers avant la comparaison.
|
 |
 |
 |
Notice how the cases can be
“stacked” on top of each other to provide multiple matches for a
particular piece of code. You should also be aware that it’s essential to
put the break statement at the end of a particular case, otherwise
control will simply drop through and continue processing on the next
case.
|
 |
Remarquez la manière d'empiler les instructions case pour
affecter un même code d'exécution à plusieurs cas d'égalité. Il faut également faire attention à
terminer chaque instruction par une instruction break, faute de quoi le contrôle
serait transféré à l'instruction correspondant au case suivant.
|
 |
 |
 |
Calculation details
|
 |
Détails de calcul :
|
 |
 |
 |
The statement:
|
 |
L'instruction :
|
 |
 |
 |
char c = (char)(Math.random() * 26 + 'a');
|
 |
char c = (char)(Math.random() * 26 + 'a');
|
 |
 |
 |
deserves a closer look. Math.random( ) produces a double, so the value 26 is converted to a double to perform the multiplication, which also produces a double. This means that ‘a’ must be converted to a double to perform the addition. The double result is turned back into a char with a cast.
|
 |
mérite une attention particulière. Math.random( ) a
pour résultat un double, par suite la valeur 26 est convertie en
double avant que la multiplication ne soit effectuée ; cette dernière a aussi pour
résultat un double. Ce qui signifie que 'a' doit lui aussi être
converti en double avant d'exécuter l'addition. Le résultat
double est finalement transtypé en char.
|
 |
 |
 |
What does the cast to char do? That is, if you have the value 29.7 and you cast it to a char, is the resulting value 30 or 29? The answer to this can be seen in this example:
|
 |
Que fait exactement ce transtypage ? En d'autres termes, si on obtient
une valeur de 29.7 et qu'on la transtype en char, le résultat est-il 30 ou
29 ? La réponse est dans l'exemple suivant :
|
 |
 |
 |
//: c03:CastingNumbers.java // What happens when you cast a float // or double to an integral value?
public class CastingNumbers { public static void main(String[] args) { double above = 0.7, below = 0.4; System.out.println("above: " + above); System.out.println("below: " + below); System.out.println( "(int)above: " + (int)above); System.out.println( "(int)below: " + (int)below); System.out.println( "(char)('a' + above): " + (char)('a' + above)); System.out.println( "(char)('a' + below): " + (char)('a' + below)); } } ///:~
|
 |
//: c03:CastingNumbers.java // Qu'arrive-t-il lorsqu'on transtype un flottant // ou un double vers une valeur entière ?
public class CastingNumbers { public static void main(String[] args) { double above = 0.7, below = 0.4; System.out.println("above: " + above); System.out.println("below: " + below); System.out.println( "(int)above: " + (int)above); System.out.println( "(int)below: " + (int)below); System.out.println( "(char)('a' + above): " + (char)('a' + above)); System.out.println( "(char)('a' + below): " + (char)('a' + below)); } } ///:~
|
 |
 |
 |
The output is:
|
 |
Le résultat :
|
 |
 |
 |
above: 0.7 below: 0.4 (int)above: 0 (int)below: 0 (char)('a' + above): a (char)('a' + below): a
|
 |
above: 0.7 below: 0.4 (int)above: 0 (int)below: 0 (char)('a' + above): a (char)('a' + below): a
|
 |
 |
 |
So the answer is that
casting from a float or double to an
integral value always truncates.
|
 |
La réponse est donc : le transtypage d'un type float
ou double vers une valeur entière entraîne une troncature dans tous les
cas.
|
 |
 |
 |
A second question concerns
Math.random( ). Does
it produce a value from zero to one, inclusive or exclusive of the value
‘1’? In math lingo, is it (0,1), or [0,1], or (0,1] or [0,1)? (The
square bracket means “includes” whereas the parenthesis means
“doesn’t include.”) Again, a test program might provide the
answer:
|
 |
Une seconde question à propos de
Math.random( ) : cette méthode produit des nombres entre zéro et un,
mais : les valeurs 0 et 1 sont-elles inclues ou exclues ? En jargon mathématique,
obtient-on ]0,1[, [0,1], ]0,1] ou [0,1[ ? (les crochets « tournés vers le nombre »
signifient « ce nombre est inclus », et ceux tournés vers l'extérieur « ce nombre
est exclu »). À nouveau, un programme de test devrait nous donner la réponse :
|
 |
 |
 |
//: c03:RandomBounds.java // Does Math.random() produce 0.0 and 1.0?
public class RandomBounds { static void usage() { System.out.println("Usage: \n\t" + "RandomBounds lower\n\t" + "RandomBounds upper"); System.exit(1); } public static void main(String[] args) { if(args.length != 1) usage(); if(args[0].equals("lower")) { while(Math.random() != 0.0) ; // Keep trying System.out.println("Produced 0.0!"); } else if(args[0].equals("upper")) { while(Math.random() != 1.0) ; // Keep trying System.out.println("Produced 1.0!"); } else usage(); } } ///:~
|
 |
//: c03:RandomBounds.java // Math.random() produit-il les valeurs 0.0 et 1.0 ?
public class RandomBounds { static void usage() { System.out.println("Usage: \n\t" + "RandomBounds lower\n\t" + "RandomBounds upper"); System.exit(1); } public static void main(String[] args) { if(args.length != 1) usage(); if(args[0].equals("lower")) { while(Math.random() != 0.0) ; // Essayer encore System.out.println("Produced 0.0!"); } else if(args[0].equals("upper")) { while(Math.random() != 1.0) ; // Essayer encore System.out.println("Produced 1.0!"); } else usage(); } } ///:~
|
 |
 |
 |
To run the program, you type a command
line of either:
|
 |
Pour lancer le programme, frapper en ligne de commande :
|
 |
 |
 |
java RandomBounds lower
|
 |
java RandomBounds lower
|
 |
 |
 |
or
|
 |
ou bien :
|
 |
 |
 |
java RandomBounds upper
|
 |
java RandomBounds upper
|
 |
 |
 |
In both cases you are forced to break out of the program manually, so it would appear that Math.random( ) never produces either 0.0 or 1.0. But this is where such an experiment can be deceiving. If you consider[26] that there are about 262 different double fractions between 0 and 1, the likelihood of reaching any one value experimentally might exceed the lifetime of one computer, or even one experimenter. It turns out that 0.0 is included in the output of Math.random( ). Or, in math lingo, it is [0,1).
|
 |
Dans les deux cas nous sommes obligés d'arrêter le programme manuellement,
et il semble donc que Math.random( ) ne produise jamais les valeurs
0.0 ou 1.0. Une telle expérience est décevante. Si vous remarquez [26] qu'il
existe environ 262 fractions différentes en double-précision entre 0 et 1, alors la
probabilité d'atteindre expérimentalement l'une ou l'autre des valeurs pourrait dépasser la vie
d'un ordinateur, voire celle de l'expérimentateur. Cela ne permet pas de montrer que 0.0
est inclus dans les résultats de Math.random( ). En réalité, en
jargon mathématique, le résultat est [0,1[.
|
 |
 |
 |
Summary
|
 |
Résumé
|
 |
 |
 |
This chapter concludes the study of fundamental features that appear in most programming languages: calculation, operator precedence, type casting, and selection and iteration. Now you’re ready to begin taking steps that move you closer to the world of object-oriented programming. The next chapter will cover the important issues of initialization and cleanup of objects, followed in the subsequent chapter by the essential concept of implementation hiding.
|
 |
Ce chapitre termine l'étude des fonctionnalités fondamentales qu'on
retrouve dans la plupart des langages de programmation : calcul, priorité des opérateurs,
transtypage, sélection et itération. Vous êtes désormais prêts à aborder le monde de la
programmation orientée objet. Le prochain chapitre traite de la question importante de
l'initialisation et du nettoyage des objets, et le suivant du concept essentiel consistant à cacher
l'implémentation.
|
 |
 |
 |
Exercises
|
 |
Exercices
|
 |
 |
 |
Solutions to selected exercises can be found in the electronic document The Thinking in Java Annotated Solution Guide, available for a small fee from www.BruceEckel.com.
|
 |
Les solutions des exercices sélectionnés sont dans le document électronique
The Thinking in Java Annotated Solution Guide, disponible pour un faible coût à
www.BruceEckel.com.
|
 |
 |
 |
- There are two expressions
in the section labeled “precedence” early in this chapter. Put these expressions into a program and demonstrate that they produce different results. - Put the
methods ternary( ) and alternative( ) into a working program. - From the
sections labeled “if-else” and “return”, put the methods test( ) and test2( ) into a working program. - Write a
program that prints values from one to 100. - Modify Exercise
4 so that the program exits by using the break keyword at value 47. Try using return instead. - Write a
function that takes two String arguments, and uses all the Boolean comparisons to compare the two Strings and print the results. For the == and !=, also perform the equals( ) test. In main( ), call your function with some different String objects. - Write a
program that generates 25 random int values. For each value, use an if-then-else statement to classify it as greater than, less than or equal to a second randomly-generated value. - Modify
Exercise 7 so that your code is surrounded by an “infinite” while loop. It will then run until you interrupt it from the keyboard (typically by pressing Control-C). - Write a
program that uses two nested for loops and the modulus operator (%) to detect and print prime numbers (integral numbers that are not evenly divisible by any other numbers except for themselves and 1). - Create a switch
statement that prints a message for each case, and put the switch inside a for loop that tries each case. Put a break after each case and test it, then remove the breaks and see what happens.
|
 |
- Dans la section « priorité » au début de ce chapitre, il y a
deux expressions. Utiliser ces expressions dans un programme qui montre qu'elles produisent des
résultats différents.
- Utiliser les méthodes ternary( ) et alternative( ) dans un
programme qui fonctionne.
- À partir des sections « if-else » et « return »,
utiliser les méthodes test( ) et test2( ) dans un programme qui fonctionne.
- Écrire un programme qui imprime les valeurs de un à 100.
- Modifier l'exercice 4 de manière que le programme termine avec la valeur
47, en utilisant le mot clef break. Même exercice en utilisant
return.
- Écrire une fonction prenant pour arguments deux String,
utiliser les comparaisons booléennes pour comparer les deux chaînes et imprimer le résultat. En
plus de == et !=, tester aussi equals( ). Dans la méthode main( ), appeler la fonction
avec différents objets de type String.
- Écrire un programme qui génère aléatoirement 25 valeurs entières. Pour
chaque valeur, utiliser une instruction if-then-else pour la classer (plus grande, plus petite, ou
égale) par rapport à une deuxième valeur générée aléatoirement.
- Modifier l'exercice 7 en englobant le code dans une boucle while infinie.
Il devrait alors fonctionner tant qu'on ne l'interrompt pas au moyen du clavier (classiquement avec
Ctrl-C).
- Écrire un programme utilisant deux boucles for imbriquées
ainsi que l'opérateur modulo (%) pour détecter et imprimer les nombres premiers
(les nombres entiers qui ne sont divisibles que par eux-mêmes et l'unité).
- Écrire une instruction switch qui imprime un message pour
chaque case, la mettre dans une boucle for qui teste chaque
case. Mettre un break après chaque case, tester,
puis enlever les break et voir ce qui se passe..
|
 |
 |
 |
[25] John Kirkham writes, “I started computing in 1962 using FORTRAN II on an IBM 1620. At that time, and throughout the 1960s and into the 1970s, FORTRAN was an all uppercase language. This probably started because many of the early input devices were old teletype units that used 5 bit Baudot code, which had no lowercase capability. The ‘E’ in the exponential notation was also always upper case and was never confused with the natural logarithm base ‘e’, which is always lowercase. The ‘E’ simply stood for exponential, which was for the base of the number system used—usually 10. At the time octal was also widely used by programmers. Although I never saw it used, if I had seen an octal number in exponential notation I would have considered it to be base 8. The first time I remember seeing an exponential using a lowercase ‘e’ was in the late 1970s and I also found it confusing. The problem arose as lowercase crept into FORTRAN, not at its beginning. We actually had functions to use if you really wanted to use the natural logarithm base, but they were all uppercase.”
|
 |
[25] John Kirkham écrivait, « J'ai fait mes débuts en
informatique en 1962 avec FORTRAN II sur un IBM 1620. À cette époque, pendant les années 60 et
jusqu'au début des années 70, FORTRAN était un langage entièrement écrit en majuscules. Sans doute
parce que beaucoup de périphériques d'entrée anciens étaient de vieux téléscripteurs utilisant le
code Baudot à cinq moments (à cinq bits), sans possibilité de minuscules. La lettre 'E' dans la
notation scientifique était en majuscule et ne pouvait jamais être confondue avec la base 'e' des
logarithmes naturels, toujours écrite en minuscule. 'E' signifiait simplement puissance, et,
naturellement, puissance de la base de numération habituellement utilisée c'est à dire puissance de
10. À cette époque également, l'octal était largement utilisé par les programmeurs. Bien que je ne
l'aie jamais vu utiliser, si j'avais vu un nombre octal en notation scientifique j'aurais pensé
qu'il était en base 8. Mon plus ancien souvenir d'une notation scientifique utilisant un 'e'
minuscule remonte à la fin des années 70 et je trouvais immédiatement cela déroutant. Le problème
apparut lorsqu'on introduisit les minuscules en FORTRAN, et non à ses débuts. En réalité il
existait des fonctions qui manipulaient la base des logarithmes naturels, et elles étaient toutes
en majuscules. »
|
 |
 |
 |
[26] Chuck Allison writes: The total number of numbers in a floating-point number system is2(M-m+1)b^(p-1) + 1where b is the base (usually 2), p is the precision (digits in the mantissa), M is the largest exponent, and m is the smallest exponent. IEEE 754 uses:M = 1023, m = -1022, p = 53, b = 2so the total number of numbers is2(1023+1022+1)2^52 = 2((2^10-1) + (2^10-1))2^52 = (2^10-1)2^54 = 2^64 - 2^54Half of these numbers (corresponding to exponents in the range [-1022, 0]) are less than 1 in magnitude (both positive and negative), so 1/4 of that expression, or 2^62 - 2^52 + 1 (approximately 2^62) is in the range [0,1). See my paper at http://www.freshsources.com/1995006a.htm (last of text).
|
 |
[26] Chuck Allison écrivait : Le nombre total de nombres
dans un système à virgule flottante est 2(M-m+1)b^(p-1) + 1 où b
est la base (généralement 2), p la précision (le nombre de chiffres dans la
mantisse), M le plus grand exposant, et m le plus petit. Dans la
norme IEEE 754, on a : M = 1023, m = -1022, p = 53, b = 2 d'où le nombre
total de nombres est 2(1023+1022+1)2^52 = 2((2^10-1) + (2^10-1))2^52 = (2^10-1)2^54 = 2^64 -
2^54 La moitié de ces nombres (correspondant à un exposant dans l'intervalle[-1022, 0])
sont inférieurs à un en valeur absolue, et donc 1/4 de cette expression, soit 2^62 - 2^52 + 1
(approximativement 2^62) est dans l'intervalle [0,1[. Voir mon article à
http://www.freshsources.com/1995006a.htm (suite de l'article).
|
 |
 |
 |
 |
 |
 |
 |
 |
|
 |
 |
 |