Le flot de contrôle
Choisir une branche avec IF et SELECT, répéter avec FOR, WHILE et REPEAT, sortir d'une boucle avant la fin, et sauter avec GOSUB et GOTO.
En un mot#
Un programme s'exécute de haut en bas. Les mots-clés de cette page changent cet
ordre : ils choisissent une branche, répètent un bloc, sortent d'une boucle ou
sautent vers une étiquette. Les mots-clés ignorent la casse : endif, EndIf
et ENDIF sont le même mot.
| Construction | S'ouvre par | Se ferme par |
|---|---|---|
| IF sur une ligne | IF … THEN … | la fin de la ligne |
| IF en bloc | IF … | ENDIF ou END IF |
| SELECT | SELECT … | ENDSELECT ou END SELECT |
| FOR | FOR … TO … | NEXT |
| FOREACH | FOREACH list() | NEXT, voir Tableaux, listes et dictionnaires |
| WHILE | WHILE … | ENDWHILE, END WHILE ou WEND |
| REPEAT | REPEAT | UNTIL … |
| DO | DO | LOOP, la boucle d'image, voir La boucle de jeu et les tâches |
Un bloc ne crée pas de portée de variable. Une variable écrite pour la première fois dans un IF ou une boucle reste lisible après. Voir Variables et types.
IF sur une ligne#
IF <condition> THEN <instruction> [: <instruction>…] [ELSE <instruction> [: <instruction>…]]
score = 75
IF score >= 50 THEN Console("pass")
IF score > 90 THEN Console("A") ELSE Console("not an A")
IF score > 50 THEN bonus = 10 : lives = 3 ELSE bonus = 0 : lives = 1
Console(Str(bonus) + " " + Str(lives)) ' 10 3
THENest obligatoire sur cette forme. Il marque la fin de la condition. Sans lui, la ligne ouvre un IF en bloc et le compilateur réclame unENDIF.- Les instructions reliées par
:aprèsTHENappartiennent toutes à la branche THEN. AprèsELSE, elles appartiennent toutes à la branche ELSE. - Dans une condition,
=compare. Il n'affecte jamais.AND,ORetNOTsont les mots logiques. Voir Constantes et opérateurs.
⚠️ Un IF sur une ligne ne peut pas contenir un autre IF sur une ligne. Le compilateur accepte la ligne, mais le programme produit est cassé :
x = 1
y = 2
IF x = 1 THEN IF y = 2 THEN Console("both")
Reliez les deux conditions par AND, ou écrivez un bloc :
x = 1
y = 2
IF x = 1 AND y = 2 THEN Console("both") ' bothIF en bloc#
IF <condition> [THEN]
<instructions>
[ELSEIF <condition> [THEN]
<instructions>]…
[ELSE
<instructions>]
ENDIF
score = 75
IF score >= 90
Console("A")
ELSEIF score >= 70 THEN
Console("B")
ELSE IF score >= 50
Console("C")
ELSE
Console("F")
END IF
' B
| Partie | Graphies acceptées |
|---|---|
| Ouverture | IF <condition> ou IF <condition> THEN, sans rien après THEN |
| Branche intermédiaire | ELSEIF ou ELSE IF, avec ou sans THEN |
| Dernière branche | ELSE |
| Fermeture | ENDIF ou END IF |
- La première branche vraie s'exécute. Les autres sont sautées.
THENest facultatif sur la forme bloc. Les programmes AGK l'omettent d'habitude, et ils compilent tels quels.ELSEvient une seule fois, en dernier. UnELSEIFaprèsELSEest une erreur de compilation, tout comme un secondELSE.- Une ligne
IF … THENsuivie de quelque chose aprèsTHENest un IF sur une ligne. Sans rien aprèsTHEN, elle ouvre un bloc.
SELECT#
SELECT [CASE] <expression>
CASE <valeur> [, <valeur>…]
<instructions>
[ENDCASE]
…
[CASE DEFAULT
<instructions>]
ENDSELECT
weapon = 2
SELECT weapon
CASE 1
Console("sword")
CASE 2, 3
Console("bow")
CASE DEFAULT
Console("fists")
ENDSELECT
' bow
La même chose, avec les graphies longues et une chaîne :
name$ = "bob"
SELECT CASE name$
CASE "alice"
Console("hi alice")
ENDCASE
CASE "bob"
Console("hi bob")
ENDCASE
DEFAULT CASE
Console("who?")
ENDCASE
END SELECT
' hi bob
| Partie | Graphies acceptées |
|---|---|
| Ouverture | SELECT <expression> ou SELECT CASE <expression> |
| Branche par défaut | CASE DEFAULT ou DEFAULT CASE |
| Fin d'une branche (facultative) | ENDCASE ou END CASE |
| Fermeture | ENDSELECT ou END SELECT |
Une branche s'exécute, et une seule. Il n'y a pas de chute vers la suivante : un
CASEvide ne fait rien et n'exécute pas le suivant. Pour partager un corps, listez les valeurs :CASE 2, 3.
- La correspondance est une égalité stricte. Une chaîne ne correspond jamais
à un nombre :
"5"ne correspond pas àCASE 5.2.0correspond àCASE 2, parce que c'est le même nombre. - Une valeur de
CASEest une expression : un littéral, une variable,2 * 2. Les valeurs sont essayées dans l'ordre. CASE DEFAULTs'exécute quand rien d'autre n'a correspondu. Il vient une seule fois, en dernier : unCASEaprès lui est une erreur de compilation.ENDCASEferme une branche. Il est facultatif : leCASEsuivant la ferme aussi.EXITdans un SELECT sort de la boucle qui l'entoure, pas seulement du SELECT. Voir EXIT et CONTINUE.
Non pris en charge, et quoi écrire à la place :
| Vous connaissez peut-être | Dans aukimi |
|---|---|
CASE 1 TO 5 (VB, PureBasic) | une chaîne IF … ELSEIF |
CASE IS > 5 (VB) | une chaîne IF … ELSEIF |
CASE ELSE (VB), Default (PureBasic) | CASE DEFAULT |
x = 3
SELECT x
CASE 1 TO 5
Console("low")
ENDSELECT
x = 3
IF x >= 1 AND x <= 5
Console("low")
ELSE
Console("high")
ENDIF
' low
⚠️ CASE IS > 5, CASE ELSE et un DEFAULT seul compilent tous les trois, et
tournent mal tous les trois à l'exécution. Les deux premiers arrêtent le
programme sur une erreur quand ils sont testés. Un DEFAULT seul n'est pas une
branche du tout : ses lignes se retrouvent dans le CASE du dessus, et ce CASE
s'arrête sur une erreur quand il s'exécute.
⚠️ N'écrivez pas de comparaison avec = dans un CASE. L'astuce
SELECT TRUE d'autres dialectes ne marche pas ici : CASE x = 7 range 7 dans
x au lieu de comparer.
FOR … NEXT#
FOR <var> = <début> TO <fin> [STEP <pas>]
<instructions>
NEXT [<var>]
total = 0
FOR i = 1 TO 5
total = total + i
NEXT i
Console(Str(total)) ' 15
FOR n = 10 TO 1 STEP -3
Console(Str(n)) ' 10, 7, 4, 1
NEXT
<fin>et<pas>sont lus une fois, avant le premier passage. Modifier une variable qui y figure ne change pas la boucle.
count = 3
FOR i = 1 TO count
count = 10
Console(Str(i)) ' 1, 2, 3
NEXT
- Le signe du pas choisit le sens. Un pas positif monte tant que le compteur
est
<=à la fin. Un pas négatif descend tant qu'il est>=à la fin. - La boucle peut ne jamais tourner.
FOR i = 5 TO 1ne fait rien : le pas vaut+1et 5 a déjà dépassé 1. STEP 0arrête le programme sur l'erreur « BASIC FOR STEP cannot be zero ». Ce n'est pas une boucle sans fin.- Le pas peut être décimal :
FOR t# = 0 TO 1 STEP 0.25donne 0, 0.25, 0.5, 0.75, 1. - Le compteur est une variable ordinaire. On peut le modifier dans la boucle, et le passage suivant ajoute le pas à la nouvelle valeur.
NEXTseul ferme le FOR le plus intérieur.NEXT idoit nommer le compteur de ce FOR, sinon le compilateur répond « NEXT variable j does not match FOR variable i ».
Le compteur après la boucle garde sa dernière valeur :
| La boucle… | Le compteur après NEXT |
|---|---|
| est allée au bout | fin + pas (FOR i = 1 TO 5 laisse 6) |
a été quittée par EXIT | sa valeur au moment de l'EXIT |
| n'a jamais tourné | la valeur de départ |
FOR i = 1 TO 10
IF i * i > 20 THEN EXIT
NEXT
Console(Str(i)) ' 5
⚠️ Défaut connu. Quand le compteur est relu après NEXT, la fin et le pas
de la boucle sont gardés à un seul endroit, partagé par toutes les exécutions de
cette boucle. Une FUNCTION qui s'appelle elle-même depuis une telle boucle peut
écourter la boucle extérieure. Dans ce cas, copiez la valeur utile dans votre
propre variable à l'intérieur de la boucle, et ne relisez pas le compteur après
NEXT.
WHILE … ENDWHILE#
WHILE <condition>
<instructions>
ENDWHILE
END WHILE et WEND la ferment aussi.
n = 1
WHILE n < 100
n = n * 2
ENDWHILE
Console(Str(n)) ' 128
La condition est testée avant chaque passage : le corps peut ne jamais s'exécuter.
REPEAT … UNTIL#
REPEAT
<instructions>
UNTIL <condition>
tries = 10
REPEAT
tries = tries + 1
UNTIL tries > 5
Console(Str(tries)) ' 11
La condition est testée après chaque passage : le corps s'exécute au moins une fois. La boucle s'arrête quand la condition devient vraie.
Il n'y a pas de DO WHILE, DO UNTIL, LOOP WHILE ni LOOP UNTIL. Écrivez
WHILE … ENDWHILE ou REPEAT … UNTIL. Dans aukimi, DO … LOOP est réservé à la
boucle d'image :
n = 0
DO WHILE n < 3
n = n + 1
LOOPDO … LOOP#
DO … LOOP est la boucle d'image d'un programme classique. Elle apparaît une
seule fois, au premier niveau, et son corps doit appeler Sync() pour qu'une
image puisse être dessinée. Elle a sa propre page :
La boucle de jeu et les tâches.
EXIT et CONTINUE y marchent comme dans toute boucle, et le code placé après
LOOP s'exécute une fois qu'EXIT en est sorti.
EXIT et CONTINUE#
EXIT
CONTINUE
total = 0
FOR i = 1 TO 10
IF Mod(i, 2) = 0 THEN CONTINUE
IF i > 7 THEN EXIT
total = total + i
NEXT
Console(Str(total)) ' 16 (1 + 3 + 5 + 7)
- Les deux agissent sur la boucle la plus intérieure : FOR, FOREACH, WHILE, REPEAT, ou la boucle d'image DO.
EXITquitte la boucle. Le programme reprend après sonNEXT,ENDWHILE,UNTILouLOOP.CONTINUElance le passage suivant. Dans un FOR, le pas est ajouté d'abord. Dans un WHILE ou un REPEAT, la condition est testée d'abord.- Hors de toute boucle, les deux sont des erreurs de compilation : « EXIT without an active loop », « CONTINUE without an active loop ». Y compris dans un SELECT qui n'est dans aucune boucle.
Dans un SELECT, ils agissent toujours sur la boucle.
s = 0
FOR i = 1 TO 5
SELECT i
CASE 3
EXIT
CASE DEFAULT
s = s + i
ENDSELECT
NEXT
Console(Str(s)) ' 3, la boucle s'est arrêtée à i = 3
Il n'y a qu'une graphie. EXIT FOR, EXIT WHILE et EXIT DO (QBasic, VB)
ne sont pas pris en charge, pas plus que Break (PureBasic, C). Écrivez EXIT :
FOR i = 1 TO 3
EXIT FOR
NEXT
EXIT quitte une seule boucle. Pour sortir de deux boucles imbriquées d'un coup,
posez un drapeau et testez-le dans la boucle extérieure, ou sautez en avant avec
GOTO.
⚠️ CONTINUE dans la boucle d'image saute le Sync(). Si le CONTINUE se
déclenche à chaque passage, la boucle ne rend jamais d'image et le jeu se fige.
Placez Sync() avant le CONTINUE, ou utilisez plutôt IF … ELSE.
END#
END
END arrête le programme.
lives = 0
Console("start")
IF lives = 0
END
ENDIF
Console("never shown")
' start
- Dans un programme qui a une boucle DO,
ENDarrête la boucle et tout ce qui suit. - Dans un sous-programme GOSUB,
ENDarrête tout le programme, pas seulement le sous-programme. Dans un programme qui a une boucle DO, l'éditeur le signale alors comme une erreur nommée « BASIC END », alors que l'arrêt était voulu. - Dans une FUNCTION,
ENDest une erreur de compilation. Quittez la fonction avecEXITFUNCTION, voir Les fonctions. ENDn'est pas une erreur : unCATCHne l'arrête donc pas. Voir Les erreurs.
⚠️ Écrivez END seul sur sa ligne. Dans un programme sans boucle DO,
IF lives = 0 THEN END sur une ligne est ignoré sans un mot, sauf si un END
seul sur sa ligne existe aussi ailleurs dans le programme. Un IF en bloc, comme
ci-dessus, marche toujours.
Les étiquettes#
<nom>:
Une étiquette nomme un endroit du programme. Elle est seule sur sa ligne.
- Une étiquette est la cible de
GOSUB,GOTOetSTARTTASK. Dans uneDATASECTION, elle est la cible deRESTORE, voir Tableaux, listes et dictionnaires. - Les noms d'étiquettes ignorent la casse. Deux étiquettes du même nom sont une erreur de compilation : « Duplicate BASIC label ».
- Une étiquette peut porter un court corps sur la même ligne, relié par
::AddFour: value = value + 4 : RETURN.
⚠️ Une étiquette ne peut pas porter le nom d'une commande. Second: est
refusée, parce que Second est une commande du catalogue, et le message
d'erreur réclame des parenthèses. Choisissez un autre nom, comme StepTwo:.
GOSUB et RETURN#
GOSUB <étiquette>
<étiquette>:
<instructions>
RETURN
score = 10
GOSUB AddBonus
GOSUB AddBonus
Console(Str(score)) ' 30
END
AddBonus:
score = score + 10
RETURN
- Un sous-programme s'exécute de son étiquette jusqu'à son
RETURN, puis le programme reprend après leGOSUB. - Un sous-programme partage toutes les variables du programme. Il n'a ni paramètres ni variables locales. Quand il vous en faut, écrivez plutôt une FUNCTION, voir Les fonctions.
- Le programme principal n'entre jamais par accident dans un
sous-programme. Les sous-programmes sont mis à part du flot principal : un
ENDoublié avant eux ne fait aucun dégât. LeENDci-dessus reste une bonne habitude. - Un
GOSUBvers une étiquette qui n'existe pas est une erreur de compilation : « Unknown BASIC label ».
Une étiquette sans RETURN continue dans l'étiquette suivante. Le RETURN de
la suivante revient alors au GOSUB :
GOSUB StepOne
Console("back")
END
StepOne:
Console("one")
StepTwo:
Console("two")
RETURN
' one, two, back
La dernière étiquette d'une telle chaîne doit finir par RETURN. Sinon, le
compilateur répond « BASIC label … requires RETURN ».
⚠️ Un sous-programme a un seul RETURN, à la fin. Un RETURN anticipé dans un
IF casse le programme. Sur une ligne (IF x > 5 THEN RETURN), le programme
s'arrête sur une erreur en atteignant cette ligne. Dans un IF en bloc, le
compilateur coupe le sous-programme à ce RETURN, et le reste du programme est
perdu sans aucun message. Mettez plutôt la suite du sous-programme dans le IF :
x = 3
GOSUB Grow
Console(Str(x)) ' 4
END
Grow:
IF x <= 5
x = x + 1
ENDIF
RETURN
Les sous-programmes qui attendent#
Un sous-programme peut appeler Sync(), Swap(), Wait(), Sleep() ou
YieldTask(). Il attend alors des images, tout comme un sous-programme qui
l'appelle. C'est permis à trois endroits :
- dans un programme qui a une boucle DO, partout, y compris avant le
DO; - dans un programme sans boucle DO qui contient un
ENDseul sur sa ligne ; - dans un autre sous-programme qui attend.
Partout ailleurs, le compilateur répond « GOSUB … yields a frame and needs the DO/LOOP main loop ».
GOSUB Loading
DO
Print("Ready")
Sync()
LOOP
Loading:
FOR part = 1 TO 3
Print("Loading " + Str(part) + "/3")
Sync()
NEXT
RETURN
RETURN <valeur>termine une étiquette de tâche et donne son résultat à la tâche. Voir La boucle de jeu et les tâches.- Dans une FUNCTION,
RETURNquitte la fonction. Voir Les fonctions. ON … GOSUBetON … GOTOne sont pas pris en charge. Utilisez un SELECT avec unGOSUBpar CASE.
GOTO#
GOTO <étiquette>
IF <condition> THEN GOTO <étiquette> [ELSE GOTO <étiquette>]
Un saut en arrière répète du code :
n = 0
again:
n = n + 1
IF n < 3 THEN GOTO again
Console(Str(n)) ' 3
Un saut en avant saute du code, et peut sortir de boucles :
FOR i = 1 TO 10
IF i * 3 > 7 THEN GOTO found
NEXT
Console("not found")
found:
Console("found " + Str(i)) ' found 3
JavaScript n'a pas de goto : le compilateur transforme donc chaque saut en une
boucle ou en un bloc dont on peut sortir. Cela marche pour les formes courantes,
et les autres sont refusées :
| Saut | Permis ? |
|---|---|
| en arrière, vers une étiquette au même niveau de bloc | oui : il répète le code entre les deux |
| en avant, vers une étiquette plus loin dans le même bloc ou dans un bloc englobant | oui : il saute le code entre les deux, et peut sortir de boucles |
| depuis un sous-programme, vers une étiquette du programme principal | oui : le programme repart de cette étiquette (voir plus bas) |
| dans un bloc (un IF, un FOR, un WHILE…) depuis l'extérieur | non : « GOTO … jumps across block boundaries » |
| en arrière, de l'intérieur d'un bloc vers une étiquette hors de lui | non : même erreur |
Repartir depuis un sous-programme. Un GOTO écrit dans un sous-programme,
vers une étiquette du programme principal, abandonne le sous-programme et repart
de cette étiquette. C'est le motif classique « rejouer » :
round = 0
restart:
round = round + 1
Console("round " + Str(round))
GOSUB Play
Console("over")
END
Play:
IF round < 3
GOTO restart
ENDIF
RETURN
' round 1, round 2, round 3, over
⚠️ Des limites que le compilateur ne détecte pas. Ces formes compilent, et le programme qu'elles produisent est cassé :
- Deux sauts en avant vers deux étiquettes différentes dans le même bloc. Une
seule cible en avant par bloc fonctionne. Cela inclut
IF c THEN GOTO a ELSE GOTO bquand les deux étiquettes viennent plus loin. Utilisez plutôt IF … ELSE. - Un saut de reprise sur une ligne. Dans un sous-programme,
IF c THEN GOTO restartne marche pas : mettezGOTO restartseul sur sa ligne dans un IF en bloc, comme ci-dessus. - Un GOTO dans le code placé après
LOOP. Il échoue à l'exécution.
x = 1
IF x = 1 THEN GOTO skipOne
Console("1")
skipOne:
IF x = 1 THEN GOTO skipTwo
Console("2")
skipTwo:
Console("end")
Préférez les boucles,
EXITetCONTINUE.GOTOexiste pour que les programmes classiques tournent, et c'est la construction qui a le plus de limites.
Si vous venez d'un autre BASIC#
| Vous écrivez peut-être | Dans aukimi |
|---|---|
EXIT FOR, EXIT WHILE, EXIT DO, Break | EXIT |
Continue (PureBasic) | CONTINUE, le même mot |
CASE 1 TO 5, CASE IS > 5 | IF … ELSEIF |
CASE ELSE, Default | CASE DEFAULT |
DO WHILE …, LOOP UNTIL … | WHILE … ENDWHILE, REPEAT … UNTIL |
Wend | accepté |
ON n GOSUB a, b | SELECT n avec un GOSUB par CASE |
IF x THEN IF y THEN … | IF x AND y THEN … |
RETURN au milieu d'un sous-programme | IF … ENDIF autour de la suite |