Suivre le Flot d’Instructions
Dans le premier article de cette série, C pour Cocoa, nous sommes passé d’une ligne de code à une deuxième ligne de code, en précisant que la seconde serait exécutée après la première.
Notre programme sait faire les choses que nous lui demandons, dans l’ordre que nous le lui demandons.
Dans le deuxième article, nous avons appris comment modifier cette approche séquentielle en utilisant des lignes de code appelées fonctions où peuvent être rassemblées un grand nombre de lignes de code.
Notre programme sait que nous pouvons lui demander des choses qui nécessitent plus d’une seule étape.
Bien que nous n’en avons pas parlé à ce moment précis, ces deux articles mettent en évidence le même concept, dont nous allons parler aujourd’hui, qui est la séquence d’exécution.
Dans notre premier programme, comme chaque ligne se trouve dans une séquence, cette ligne était celle où le programme “était”, et elle est exécutée avant que le programme ne passe à la ligne suivante, et ne recommence le même processus.
Mais parfois, nous ne souhaitons pas qu’il exécute toutes les lignes de code.
Parfois, nous souhaitons qu’il exécute cette ligne de code quand l’ordinateur est connecté au réseau, et cette ligne s’il ne l’est pas.
Parfois nous voulons que l’ordinateur effectue un choix parmi plusieurs solutions, en fonction du contexte.
Parfois, nous voulons que le programme effectue une même chose plusieurs fois, comme quand l’utilisateur n’est pas occupé, ou autant de fois que les touches ont été pressées.
Dans tout ces cas, nous souhaitons modifier la séquence d’exécution de notre programme.
Nous voulons que l’ordinateur effectue quelque chose qui n’est pas simplement une liste de commandes, effectuée une fois pour toute.
Nous voulons que notre programme effectue ce qui est approprié selon les circonstances, et ne s’occupe pas du reste.
C’est ce que nous allons voir dans cette leçon.
Apprendre à Dire Non
Mais avant de commencer, nous devons apprendre à dire non.
Quand vous posez une question simple, vous attendez une réponse simple.
Mais comme nous l’avons vu dans la première leçon, vous ne pouvez pas poser une question à l’ordinateur, et votre code ne vous en posera certainement pas.
Votre programme peut poser des questions à l’utilisateur, mais le programmeur ne peut qu’écrire des ordres à effectuer.
La solution à ce problème est empruntée à un vieux professeur irlandais nommé George Boole, qui a inventé une manière d’exprimer “oui” et “non” appelée la logique booléenne.
Traditionnellement, en C, on y fait référence par TRUE et FALSE (remarque : vous devez utiliser les majuscules), mais Objective-C utilise YES et NO.
Dans cette leçon, nous utiliserons la version C parce qu’elle est plus facile à lire dans un texte.
Vous pourriez penser que la meilleure façon de faire serait de créer un type booléen qui gère les distinctions TRUE/FALSE.
Et en effet, dans un monde parfait, cela serait fait exactement comme cela.
Dans les langages modernes, c’est ce qui est fait.
En C cependant, nous devons un peu tricher.
Au lieu d’avoir un type particulier, on utilise un type qui existe déjà et on fait comme si.
On utilise le maintenant familier int, qui quand il est égale à zéro, est interprété comme FALSE ; quand ce n’est pas le cas, il est interprété comme TRUE.
Cela peut paraitre un peu étrange, mais cela peut amener à des simplifications que nous verrons dans d’autres leçons à venir.
Mais pour le moment, commençons cette leçon et apprenons à contrôler notre séquence d’exécution.
Si C’Est Codé, Ça Sera Exécuté
Le plus simple élément de structure de contrôle est le bloc if.
Il est si simple que nous n’avons probablement pas besoin de trop détailler le bloc de code suivant :
int integerForSeedValue(int seedNumber) {
int retval = 0;
if (1) {
printf("This is always true.n");
retval = seedNumber - 3;
}
return retval;
}
Vous devriez utiliser cet exemple et les suivants dans votre code pour modifier la fonction integerForSeedValue que nous avons écrite la leçon passée.
Chacun des blocs de code suivants fonctionne et effectue des choses un peu différentes.
Au fur et à mesure que nous les abordons, modifier votre programme avec le code de l’exemple.
J’ai ajouté une déclaration de la variable retval, qui est la valeur que la fonction retournera (c’est un nom très utilisé pour une telle variable).
Remarquez également que la variable est retournée à la fin de la fonction.
Le bloc conditionnel se situe au milieu.
Ici, nous avons ajouté if, puis quelque chose entre parenthèses, et un bloc de code.
La clé du code est ce quelque chose entre les parenthèses.
Cela s’appelle notre condition, et si elle est TRUE, le code situé dans le bloc sera exécuté, et la fonction retournera seedNumber -3.
Si ce n’est pas le cas, l’ordinateur ignorera complètement le bloc, et la fonction retournera 0 qui est la valeur de retval dans sa déclaration.
Puisque notre condition ici est la constante 1, le bloc est toujours exécuté.
Rendons le code un peu plus intéressant :
int integerForSeedValue(int seedNumber) {
int retval = 0;
int cond = 17 < 23;
if (cond) {
printf("What do you know: 17 *is* less than 23!n");
retval = seedNumber - 3;
}
return retval;
}
Nous avons ajouté une ligne de code qui nous semble familière : c’est une déclaration de variable qui crée une variable nommée cond.
Mais cela est effectué de manière nouvelle pour nous.
Au lieu des opérateurs arithmétiques classiques, nous avons l’opérateur de comparaison “<”, inférieur à, que vous utilisez pour savoir quel nombre est plus grand que les autres.
Comme tous les opérateurs, il opère sur ce qui l’entoure.
S’il s’agissait d’un signe plus d’addition, il ajouterait les deux éléments autour de lui, mais comme il s’agit de l’opérateur inférieur à, il les compare.
Si l’élément à sa gauche est inférieur à l’élément à sa droite, il retourne un booléen dont la valeur est TRUE.
Comme 17 est toujours inférieur à 23, la première ligne initialise cond à TRUE.
Ensuite, quand nous arrivons à notre condition if, comme cond est vraie, le bloc est exécuté.
Si vous changiez l’opérateur de comparaison par supérieur à , cond serait initialisée à FALSE et le bloc ne serait jamais exécuté.
Ce qui serait utilisé dans ce cas est le bloc optionnel else, que nous pouvons voir dans le code ci-dessous :
int integerForSeedValue(int seedNumber) {
int retval = 0;
int cond = 17 > 23; //note we're now using the greater-than operator
if (cond) {
printf("What do you know: 17 *is* greater than 23!n");
retval = seedNumber - 3;
} else {
printf("Well lookie here: 17 is *less than* 23!n");
retval = seedNumber * 3;
}
return retval;
}
Le bloc else sera exécuté chaque fois que le bloc if n’est pas exécuté — et vice-versa.
Mais notez qu’aucun des deux n’a besoin d’être un bloc.
Vous pouvez aussi simplement ajouter une simple ligne de code après la condition if ou else, et cette ligne de code sera exécutée comme un bloc :
int integerForSeedValue(int seedNumber) {
int retval = 0;
int cond = 17 > 23;
//no printf's here; only one line statements
if (cond) retval = seedNumber - 3;
else retval = seedNumber * 3;
return retval;
}
Il est de bonne pratique d’ajouter un bloc, parce qu’en l’enlevant, vous pouvez rencontrer des soucis plus tard quand vous souhaitez ajouter du code d’un côté ou de l’autre de la conditionnelle et que vous oubliez alors d’ajouter les parenthèses.
Il est donc préférable de prendre comme habitude d’ajouter des parenthèses que vous en ayez besoin tout de suite ou non.
Mais la plupart du temps, la logique dont vous avez besoin est plus complexe qu’une simple ligne ou qu’un choix.
C’est souvent ceci, ou cela, ou cette autre chose.
Dans ce cas, nous enchainons les instructions if/else pour permettre de plus en plus de décisions, comme cela :
int integerForSeedValue(int seedNumber) {
int retval = 0;
int cond = 17 > 23;
if (cond) {
printf("Amazing! 17 is *greater than* 23! My life has changedn");
retval = seedNumber - 3;
} else if ((seedNumber < 5) && (seedNumber > -5)) {
printf("That there seed number is real close to zero!n");
retval = seedNumber * 2;
} else {
printf("No special cases here; do what we normally do!n");
return = seedNumber * 3;
}
return retval;
}
La seconde instruction if est uniquement testée si la première échoue, ce qui est toujours le cas puisque 17 n’est jamais supérieur à 23.
Mais ici, nous effectuons également deux nouvelles choses importantes.
Premièrement, notre condition inclut le paramètre d’entrée de la fonction.
Ceci comme vous pouvez l’imaginer est beaucoup plus utile que de définir des variables qui seront toujours TRUE ou FALSE comme nous en avons définies auparavant.
Deuxièment, nous avons écrit des trucs complètement délirant avec des &, ce qui est complètement nouveau.
Ce que nous avons fait, c’est de créer une condition composée.
Maintenant, au lieu de vérifier une seule condition, nous en vérifions deux : maintenant nous nous assurons que seedNumber est inférieur à 5 et est supérieur à -5.
Ce double “et” commercial représente le booléen et.
Pourquoi pas un seul “et” commercial ? Il est utilisé pour quelque chose d’autre, dont vous n’aurez pas besoin de vous soucier avant longtemps.
Revenez aux exemples des blocs if/else et essayez les dans votre programme ; chacun fonctionne un peu différemment, alors essayez chacun d’eux si vous ne l’avez pas fait.
Dans chaque exemple, regardez quel code est et n’est pas exécuté pour voir la séquence d’exécution.
Une fois terminé, nous regarderons une autre façon de faire les même choses.
Maintenant Nous Ecrivons En Codé
Certaines personnes aiment à réduire la quantité de code qu’il écrive.
Cela peut-être un but admirable : moins de code signifie un fonctionnement plus rapide et de maintenance plus simple.
Parfois, la concision peut rendre votre code plus claire.
Pour atteindre cet objectif, C inclut un opérateur qui effectue en une opération ce qui d’habitude requiert un bloc if/else : initialiser une variable par deux valeurs au choix en fonction d’une condition booléenne.
Cet opérateur est appelé opérateur ternaire et transforme ce code :
int integerForSeedValue(int seedNumber) {
int retval = 0;
if (seedNumber ==
{
retval = seedNumber - 3;
} else {
retval = seedNumber * 3;
}
return retval;
}
en celui-ci :
int integerForSeedValue(int seedNumber) {
return (seedNumber ==
? (seedNumber - 3) : (seedNumber * 3);
}
C’est un gain de place considérable, mais la seconde version ressemble à une équation de physique quantique.
Eclatons cette ligne élément par élément.
Commençons par return, que nous connaissons et aimons depuis notre dernière leçon.
Puis nous avons un bout de logique entre parenthèses.
C’est la condition que nous utilisons dans le bloc if/else, exprimée de la même façon.
Si nous placions un point-virgule ici, le résultat de la condition serait retourné.
Au lieu de cela, nous avons un point d’interrogation, qui indique à l’ordinateur qu’il a lu la condition d’un opérateur ternaire, et que le reste va suivre.
La partie suivante entre parenthèse est ce que l’opérateur ternaire doit évaluer si la condition est vraie.
Elle est placée en premier, tout comme la condition vraie est en premier dans un bloc if/else.
Ensuite, nous avons deux points, qui indique à l’ordinateur que nous en avons terminé avec le cas vrai et que nous arrivons au cas faux.
Ici, nous plaçons ce qui doit être évalué par l’opérateur ternaire, si la condition est fausse.
Vous pouvez lire le point d’interrogation comme “si la condition est vraie, évaluer ceci” et les deux points comme “mais si la condition était fausse, évaluer cela”.
Mais remarquez que vous ne pouvez évaluer que des cas simples, et vous ne pouvez donc pas avoir plusieurs lignes, et chaque cas doit avoir une valeur d’évaluation du type attendu sinon vous aurez un warning
Une autre chose à noter est que nous avons introduit un nouvel opérateur, “==”, l’égalité conditionnelle.
Il vérifie si ce qui est à gauche est égale à ce qui est à droite, et s’ils le sont, il évalue le résultat à TRUE, sinon à FALSE.
Pourquoi ne pas avoir un seul signe égal ?
Parce que nous utilisons déjà cet élément pour initialiser les variables, et donc, nous ne pouvons pas l’utiliser ailleurs.
Utilisation du Switch
Quand vous écrivez du code qui doit effectuer un choix entre quelques options, if est le choix immédiat.
Mais si vous devez effectuer un choix en fonction d’une valeur, alors il est plus simple d’utiliser switch :
int integerForSeedValue(int seedNumber) {
int retval = 0;
switch (seedNumber) {
case 1:
printf("The Seed was one, not many.n");
retval = seedNumber;
break;
case 8:
printf("We have an eightfold seed.n");
retval = seedNumber * 2;
break;
}
return retval;
}
Dans cette section, nous allons également examiner différentes variantes de la fonction integerForSeedValue, et vous devriez faire fonctionner votre programme avec chacune.
Cassons le code en morceaux.
Nous commençons avec l’élément switch, puis quelque chose entre parentheses.
Après cela, vient un bloc.
Mais ce qui se trouve entre parenthèses n’est pas une condition, c’est une variable.
C’est une variable dont la valeur détermine ce qui va être évalué à l’intérieur du bloc switch.
Une fois que nous connaissons la variable, nous nous déplaçons à travers le bloc switch en se rendant à la ligne case dont la valeur correspond, en ignorant tout autre code.
Une fois trouvé, on exécute le code attaché jusqu’à rencontrer l’instruction break, qui nous fait sortir du bloc.
Notez qu’on ne sort pas du bloc en rencontrant une autre instruction case : un break est la seule chose qui nous en fera sortir.
Cela semble bizarre jusqu’à ce que vous preniez en compte ce qui suit :
int integerForSeedValue(int seedNumber) {
int retval = 0;
switch (seedNumber) {
case 1:
printf("Surreptitiously, a singular seed..n");
retval = seedNumber;
break;
case 6:
case 7:
case 8:
printf("Our seed is 6, 7, or maybe even 8!n");
retval = seedNumber * 2;
break;
}
return retval;
}
Ce qui signifie que si seedNumber est 6, 7 ou 8, retval sera initialisé à seedNumber * 2.
En d’autres termes, vous pouvez groupez des cases ensemble si vous souhaitez qu’ils effectuent le même code.
Mais remarquez que chaque case est une constante, un entier.
Ceci est une limitation importante dans la façon dont fonctionne switch : ses case ne peuvent être que des constantes et pas des expressions booléennes comme les blocs de conditions if/else
La syntaxe de switch offre une manière de gérer tout ce que vous n’avez pas pris en compte.
Le bout de code suivant en est un exemple :
int integerForSeedValue(int seedNumber) {
int retval = 0;
switch (seedNumber) {
case 1:
printf("He is The One.n");
retval = seedNumber;
break;
case 8:
printf("And the number of the seed shall be eight.n");
retval = seedNumber * 2;
default:
printf("What a seed it is!n");
retval++;
}
return retval;
}
Nous avons ajouté un cas default.
Le code sera exécuté si aucun cas, n’est satisfait, ou si aucun break n’est trouvé avant.
Remarquez que nous avons enlevé le break dans le case 8.
Ce qui permet d’exécuter default quand seedNumber est 8, ce qui est le cas dans notre programme tel qu’il est écrit dans la seconde leçon.
Mais vous avez pu constaté que le code dans notre cas par défaut ne correspond à rien de ce que nous avons déjà vu.
Le “++” est un nouvel opérateur, l’opérateur d’incrémentation.
Il prend la valeur de retval et lui ajoute 1.
Son frère, l’opérateur de décrémentation, “–“, soustrait 1.
Exécutez tous les exemples de switch différents et voyez le résultat.
Essayez de suivre l’exécution du programme.
Une Fin Conditionnelle
Nous avons appris trois manière de changer l’exécution de notre programme, chacune étant utile pour différentes situations.
Nous pouvons maintenant changer l’exécution du code en fonction d’une logique booléenne, nous pouvons initialiser des variables en se basant sur la même logique, et notre code peut changer sa manière de fonctionner en fonction de la valeur des variables.
Mais il y a plus.
Aussi utiles que ces structures soient, elles ne vous permettent pas d’effectuer des choses répétitives, ce qui est le domaine exclusif des boucles, que nous verrons dans notre prochaine leçon.

Textes originaux en anglais sur O’Reilly : Go with the Flow par Seth Roby
Chargement
Commentaires récents