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.

ConstructionS'ouvre parSe ferme par
IF sur une ligneIF … THEN …la fin de la ligne
IF en blocIF …ENDIF ou END IF
SELECTSELECT …ENDSELECT ou END SELECT
FORFOR … TO …NEXT
FOREACHFOREACH list()NEXT, voir Tableaux, listes et dictionnaires
WHILEWHILE …ENDWHILE, END WHILE ou WEND
REPEATREPEATUNTIL …
DODOLOOP, 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
  • THEN est 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 un ENDIF.
  • Les instructions reliées par : après THEN appartiennent toutes à la branche THEN. Après ELSE, elles appartiennent toutes à la branche ELSE.
  • Dans une condition, = compare. Il n'affecte jamais. AND, OR et NOT sont 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")   ' both

IF 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
PartieGraphies acceptées
OuvertureIF <condition> ou IF <condition> THEN, sans rien après THEN
Branche intermédiaireELSEIF ou ELSE IF, avec ou sans THEN
Dernière brancheELSE
FermetureENDIF ou END IF
  • La première branche vraie s'exécute. Les autres sont sautées.
  • THEN est facultatif sur la forme bloc. Les programmes AGK l'omettent d'habitude, et ils compilent tels quels.
  • ELSE vient une seule fois, en dernier. Un ELSEIF après ELSE est une erreur de compilation, tout comme un second ELSE.
  • Une ligne IF … THEN suivie de quelque chose après THEN est un IF sur une ligne. Sans rien après THEN, 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
PartieGraphies acceptées
OuvertureSELECT <expression> ou SELECT CASE <expression>
Branche par défautCASE DEFAULT ou DEFAULT CASE
Fin d'une branche (facultative)ENDCASE ou END CASE
FermetureENDSELECT ou END SELECT

Une branche s'exécute, et une seule. Il n'y a pas de chute vers la suivante : un CASE vide 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.0 correspond à CASE 2, parce que c'est le même nombre.
  • Une valeur de CASE est une expression : un littéral, une variable, 2 * 2. Les valeurs sont essayées dans l'ordre.
  • CASE DEFAULT s'exécute quand rien d'autre n'a correspondu. Il vient une seule fois, en dernier : un CASE après lui est une erreur de compilation.
  • ENDCASE ferme une branche. Il est facultatif : le CASE suivant la ferme aussi.
  • EXIT dans 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-êtreDans 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 1 ne fait rien : le pas vaut +1 et 5 a déjà dépassé 1.
  • STEP 0 arrê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.25 donne 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.
  • NEXT seul ferme le FOR le plus intérieur. NEXT i doit 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 boutfin + pas (FOR i = 1 TO 5 laisse 6)
a été quittée par EXITsa 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
LOOP

DO … 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.
  • EXIT quitte la boucle. Le programme reprend après son NEXT, ENDWHILE, UNTIL ou LOOP.
  • CONTINUE lance 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, END arrête la boucle et tout ce qui suit.
  • Dans un sous-programme GOSUB, END arrê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, END est une erreur de compilation. Quittez la fonction avec EXITFUNCTION, voir Les fonctions.
  • END n'est pas une erreur : un CATCH ne 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, GOTO et STARTTASK. Dans une DATASECTION, elle est la cible de RESTORE, 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 le GOSUB.
  • 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 END oublié avant eux ne fait aucun dégât. Le END ci-dessus reste une bonne habitude.
  • Un GOSUB vers 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 END seul 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, RETURN quitte la fonction. Voir Les fonctions.
  • ON … GOSUB et ON … GOTO ne sont pas pris en charge. Utilisez un SELECT avec un GOSUB par 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 :

SautPermis ?
en arrière, vers une étiquette au même niveau de blocoui : 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 englobantoui : il saute le code entre les deux, et peut sortir de boucles
depuis un sous-programme, vers une étiquette du programme principaloui : le programme repart de cette étiquette (voir plus bas)
dans un bloc (un IF, un FOR, un WHILE…) depuis l'extérieurnon : « GOTO … jumps across block boundaries »
en arrière, de l'intérieur d'un bloc vers une étiquette hors de luinon : 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 b quand 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 restart ne marche pas : mettez GOTO restart seul 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, EXIT et CONTINUE. GOTO existe 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-êtreDans aukimi
EXIT FOR, EXIT WHILE, EXIT DO, BreakEXIT
Continue (PureBasic)CONTINUE, le même mot
CASE 1 TO 5, CASE IS > 5IF … ELSEIF
CASE ELSE, DefaultCASE DEFAULT
DO WHILE …, LOOP UNTIL …WHILE … ENDWHILE, REPEAT … UNTIL
Wendaccepté
ON n GOSUB a, bSELECT n avec un GOSUB par CASE
IF x THEN IF y THEN …IF x AND y THEN …
RETURN au milieu d'un sous-programmeIF … ENDIF autour de la suite