Variables et types

Créer des variables, choisir un type numérique qui déborde comme vous l'attendez, activer OPTION EXPLICIT, partager l'état avec GLOBAL, et exposer un réglage dans l'Inspecteur.

En un mot#

Une variable existe dès que vous l'affectez. Elle contient n'importe quelle valeur, jusqu'à ce que vous lui donniez un type avec AS ; à partir de là, toute valeur rangée dedans est convertie dans ce type. OPTION EXPLICIT rend les déclarations obligatoires, et GLOBAL partage une variable avec vos fonctions.

Les variables implicites#

<name> = <value>
Console(Str(count))   ' 0 : lue avant sa première affectation
count = 5
Console(Str(count))   ' 5
x = 1
x = "maintenant du texte"
Console(x)            ' maintenant du texte
  • La première affectation crée la variable. Il n'y a ni DIM ni VAR pour une simple valeur.
  • Une variable lue avant sa première affectation vaut 0.
  • Une variable sans type contient n'importe quoi : un nombre, puis une chaîne, puis de nouveau un nombre. Ses nombres sont ceux de JavaScript (virgule flottante sur 64 bits).

⚠️ Un nom que le programme n'affecte jamais n'est pas une variable. total = lives + 1, avec lives affecté nulle part, compile. Le programme s'arrête ensuite sur cette ligne avec lives is not defined. C'est ainsi qu'apparaît un nom mal orthographié.

⚠️ Une variable $ lue avant sa première affectation vaut 0 elle aussi, et non une chaîne vide. Donnez-lui d'abord une valeur.

Console("[" + title$ + "]")   ' [0], pas []
title$ = "Héros"

Les sigils#

Un nom de variable peut finir par un sigil.

SigilSens classique
$texte
#nombre décimal
%entier
&entier long

Ce que fait un sigil#

  • Il choisit la valeur de départ d'une variable déclarée sans valeur : GLOBAL a, b$ donne 0 et "".
  • Il choisit ce qu'attend READ d'une ligne DATA : du texte pour $, un nombre décimal pour #, un entier sinon.
  • Il type un champ d'enregistrement. Dans un TYPE, un champ sans sigil ni AS est un entier. Voir Enregistrements et méthodes.
  • Il choisit la sorte d'un réglage exposé. Voir @expose plus bas.
  • Sur un appel de commande, il doit correspondre au résultat. Str$(12) est accepté ; un $ sur une commande qui rend un nombre est refusé.
v$ = Val$("12")
Val does not return text: drop the $ sigil

Ce que ne fait pas un sigil#

Un sigil ne convertit rien. Pour convertir, donnez un type à la variable avec AS.

n% = 2.7
Console(Str(n%))       ' 2.7 : le % n'arrondit pas
t$ = 5
Console(Str(t$ + 1))   ' 6 : le $ ne fait pas du texte

Deux noms qui ne diffèrent que par leur sigil#

obj et obj$ sont deux variables. C'est ce qu'attendent les programmes classiques.

obj = 1
obj$ = "mesh" + Str(obj) + ".obj"
Console(Str(obj) + " " + obj$)   ' 1 mesh1.obj

⚠️ Un champ d'enregistrement à sigil perd son type dès que le même nom apparaît sans le sigil ailleurs dans le programme : une variable speed, ou s.speed pour un champ déclaré speed#. Le champ devient alors un entier : 2.5 est rangé en 2, et un texte arrête le programme. Donnez à un tel champ un nom à lui, ou déclarez-le avec AS (speed AS float), qui n'a pas de sigil à perdre.

TYPE Ship
  hp
  speed#
  name$
ENDTYPE
s AS Ship
s.hp = 2.7
s.speed# = 2.5
s.name$ = "Ada"
Console(Str(s.hp) + " " + Str(s.speed#) + " " + s.name$)   ' 2 2.5 Ada

Les variables typées : AS#

[GLOBAL | LOCAL] <name> AS <type> [= <value>]
[GLOBAL | LOCAL] <name> AS <type>[<size>[, <size> ...]] [= [<value>, ...]]
lives AS integer = 3
speed AS float = 2.5
title AS string = "Héros"
hits AS ubyte
hits = 250
hits = hits + 10
Console(Str(hits))   ' 4 : 260 a fait le tour après 255
  • Sans = <valeur>, un nombre part de 0, une chaîne de "", une poignée de NULL. Certains noms de type exigent la valeur : voir les ⚠️ du tableau plus bas.
  • Le type agit à chaque rangement d'une valeur : avec =, avec +=, -=, *=, /=, et avec INC et DEC.
  • Le calcul entre deux rangements n'est pas typé. h / 2 est calculé en pleine précision ; seul le rangement le convertit.
  • Le nom d'une variable typée ne porte pas de sigil. name$ AS string compile, mais le programme ne démarre pas. Écrivez name AS string.
  • La forme tableau AS <type>[<taille>] est traitée dans Les collections, et AS <type enregistrement> dans Enregistrements et méthodes.
h AS integer = 7
h = h / 2
Console(Str(h))   ' 3 : 3.5 est rangé en 3
q = h / 2
Console(Str(q))   ' 1.5 : q n'a pas de type

Tous les types#

TypeS'écrit aussiPlageNotes
int8byte ⚠️, b−128 … 127
uint8ubyte, ascii, a ⚠️0 … 255
int16word, short, w−32 768 … 32 767
uint16uword, ushort, unicode, u ⚠️, c ⚠️0 … 65 535
int32integer, int ⚠️, long, i, l ⚠️−2 147 483 648 … 2 147 483 647long fait 32 bits
uint32uint, uinteger, dword0 … 4 294 967 295
int64quad, q−2⁶³ … 2⁶³ − 1voir 64 bits plus bas
uint64uquad0 … 2⁶⁴ − 1voir 64 bits plus bas
float32float, single, f ⚠️environ 7 chiffres significatifsfloat fait 32 bits
float64double, denviron 15 chiffres significatifsla précision d'un nombre sans type
string ⚠️stextetoute valeur est transformée en texte
handlepointer ⚠️une référence (@variable)NULL par défaut
taskhandlejobune tâche en coursNULL par défaut

Les noms de type sont insensibles à la casse. Les noms d'une lettre sont les suffixes de PureBasic.

⚠️ Les noms marqués ⚠️ exigent une valeur. Écrits seuls (x AS byte, x AS string), ils perdent leur type, parce que le compilateur les lit comme le début d'un nom de commande du catalogue (ByteLen, StringToBase64…). La variable se comporte alors comme une variable sans type qui part de 0. Écrivez x AS byte = 0 et x AS string = "", ou prenez un nom sans ⚠️.

a AS byte
a = 300
Console(Str(a))   ' 300 : aucun type n'a été appliqué
b AS int8
b = 300
Console(Str(b))   ' 44

Comment une valeur est convertie#

Rangée dansRègleExemple
un type entierles décimales tombent (vers zéro)7.9 → 7, -7.9 → -7
un type entierhors plage, la valeur fait le tourint8 : 127 + 1 → -128 ; uint16 : -1 → 65535
un type entierun texte fait de chiffres est lu"12" → 12
un type entiertout autre texte arrête le programme"abc", "1.5" → BASIC integer requires an integral value
un type entierl'infini arrête le programme1 / 0 → BASIC integer requires a finite value
float32arrondie au flottant 32 bits le plus proche0.1 → 0.10000000149011612
stringtoute valeur devient du texte42 → "42"
n AS integer = 7.9
m AS integer = -7.9
k AS integer = "12"
w AS word = 40000
Console(Str(n) + " " + Str(m) + " " + Str(k + 1) + " " + Str(w))   ' 7 -7 13 -25536

⚠️ Un compteur de FOR typé ne fait pas le tour. FOR b = 250 TO 260 avec b AS uint8 compte jusqu'à 260 et finit à 261. Le type agit de nouveau à la prochaine affectation ordinaire.

⚠️ float fait 32 bits#

Dans aukimi, float et single sont des float32, comme dans AGK. Une valeur comme 0.1 ne se range pas exactement, et Str montre tous les chiffres de ce qui est rangé.

price AS float = 0.1
Console(Str(price))      ' 0.10000000149011612
exact AS double = 0.1
Console(Str(exact))      ' 0.1
Console(Str(price, 2))   ' 0.10

Prenez double quand les décimales doivent s'afficher proprement, ou donnez à Str un nombre de décimales. Une variable sans type a déjà la double précision.

⚠️ long fait 32 bits#

long est un int32, comme le .l de PureBasic. Le Long 64 bits de Visual Basic s'écrit ici quad (int64).

big AS long = 3000000000
Console(Str(big))   ' -1294967296

Les entiers 64 bits#

int64 et uint64 contiennent des valeurs 64 bits exactes. Ce sont des nombres d'une sorte à part, qui ne se mélangent qu'entre eux.

Ce qui marche :

counter AS int64 = 5
counter = counter + 1
counter += 10
INC counter
Console(Str(counter))   ' 17
huge AS int64 = "9223372036854775807"
Console(Str(huge))      ' 9223372036854775807
five AS int64 = 5
IF counter > five THEN Console("plus grand")
FormeMarche ?
big = big + n, big += n, -=, *=, /=, INC, DECoui
big2 = big + other64 (rien que des valeurs 64 bits)oui
big = 5, big = "123"oui
big > 4, big < 10oui
⚠️ y = big + 1 (le résultat n'est pas une variable 64 bits)s'arrête : Cannot mix BigInt and other types
⚠️ other AS int64 = big * 3s'arrête : même message
⚠️ IF big = 5jamais vrai : comparez avec une autre variable 64 bits
⚠️ big = big * 2 + 1résultat faux : calculé comme big * (2 + 1) ; écrivez big *= 2 puis big += 1
⚠️ un littéral au-delà de 9 007 199 254 740 991perd ses derniers chiffres : écrivez-le en texte, "9223372036854775807"

La division entière de valeurs 64 bits laisse tomber les décimales : big /= 4 garde la partie entière. Ne prenez les types 64 bits que si 32 bits sont vraiment trop petits.

Les littéraux de chaque type#

TypeÉcrireNote
types entiers42, -7, 0x1F, 0c17, %1010une valeur décimale est coupée : 7.9 → 7
64 bits42, ou "9223372036854775807"en texte au-delà de 2⁵³
float, double2.5, .5, 1.25e2
string"texte"un nombre est transformé en texte
handle@variable, NULL

TRUE et FALSE ne sont pas des nombres : voir Constantes et opérateurs.

Les suffixes de type de PureBasic : .f, .i, .s…#

<name>.<suffix> = <value>
speed.f = 1.5
speed = speed * 2
count.i = 7.9
label.s = "x"
Console(Str(speed) + " " + Str(count) + " " + label)   ' 3 7 x
  • Le premier nom.suffixe déclare la variable avec ce type, comme si vous écriviez nom AS <type>. Les lignes suivantes peuvent laisser tomber le suffixe.
  • Les suffixes sont les noms d'une lettre du tableau plus haut : b a w u c l i q f d s.
  • Une variable qui est déjà un enregistrement, ou un objet du catalogue comme un sprite, garde ses propres champs : s.x sur un sprite n'est pas un suffixe.

⚠️ c est un uint16 sur une variable et dans une liste, mais un int32 sur un champ d'enregistrement.

OPTION EXPLICIT#

OPTION EXPLICIT [' comment]
OPTION EXPLICIT
GLOBAL lives AS integer = 3
GLOBAL score = 0
GLOBAL name$ = "Ada"
DIM grid(2)
FUNCTION Hit(damage)
  LOCAL rest = lives - damage
  lives = rest
ENDFUNCTION lives
Hit(1)
score = score + 10
grid(1) = score
Console(name$ + " " + Str(lives) + " " + Str(grid(1)))   ' Ada 2 10

OPTION EXPLICIT est désactivé par défaut, pour que les programmes importés compilent encore. Une fois activé, tout nom dans lequel vous écrivez doit être déclaré d'abord.

OPTION EXPLICIT
score = 1
OPTION EXPLICIT requires a declaration for variable: score
  • Il est seul sur sa ligne. Seul un commentaire ' peut le suivre ; OPTION EXPLICIT // note est refusé.
  • Il vaut pour tout le programme, fichiers inclus compris, où que soit la ligne.
  • Toutes les démos d'aukimi l'utilisent.

Ce qui doit être déclaré#

Écrit parExemple
une affectationscore = 1, grid(1) = 4, hero.hp = 3 (le premier nom)
un compteur de FORFOR i = 1 TO 10
INC / DECINC score
READREAD level

Ce qui compte comme déclaration#

DéclarationExemple
GLOBALGLOBAL score, GLOBAL score = 0, GLOBAL a, b$
LOCAL, dans une FUNCTIONLOCAL rest = 0
ASlives AS integer, hero AS Player
un suffixe PureBasicspeed.f = 1.5
DIMDIM grid(10)
NewList, NewMapNewList names.s()
un paramètre de FUNCTIONFUNCTION Hit(damage)
une variable de CATCHCATCH problem$

⚠️ OPTION EXPLICIT contrôle les noms où vous écrivez, pas ceux que vous lisez. Un nom mal orthographié à droite du = compile quand même, et arrête le programme quand la ligne s'exécute.

OPTION EXPLICIT
GLOBAL total = 0
total = totl + 1   ' compile ; s'arrête à l'exécution : totl is not defined

GLOBAL et LOCAL#

GLOBAL <name> [= <value>]
GLOBAL <name> AS <type> [= <value>]
GLOBAL <name>, <name> [, <name> ...]
LOCAL <name> [= <value>]
LOCAL <name> AS <type> [= <value>]
LOCAL <name>, <name> [, <name> ...]

GLOBAL DIM, LOCAL DIM, GLOBAL NewList et GLOBAL NewMap déclarent des collections de la même façon. Define et Protected de PureBasic sont acceptés comme LOCAL.

Mot-cléOùEffet
GLOBALpartout, même dans une FUNCTIONune seule variable pour tout le programme, vue par toutes les fonctions
LOCALdans une FUNCTIONune variable propre à cet appel ; elle masque une variable du programme de même nom
FUNCTION Setup()
  GLOBAL best = 100
ENDFUNCTION
Setup()
Console(Str(best))   ' 100

⚠️ Donnez une valeur à un GLOBAL seul. GLOBAL hits tout seul part vide, pas de 0, et hits + 1 donne alors NaN. La forme en liste part de 0 (ou de "").

GLOBAL hits
Console(Str(hits))     ' undefined, pas 0
GLOBAL misses = 0
Console(Str(misses))   ' 0
GLOBAL a, b$
Console(Str(a) + "[" + b$ + "]")   ' 0[]

⚠️ LOCAL a sa place dans une FUNCTION. Au premier niveau du programme, une variable LOCAL affectée de nouveau plus loin casse le programme : il ne démarre pas. Utilisez GLOBAL ou AS à cet endroit. Il en va de même pour Define.

LOCAL lives = 3
lives = 2

La portée#

Un bloc n'est pas une portée#

Une variable affectée pour la première fois dans un IF, un FOR, un WHILE ou un SELECT appartient au programme. Elle existe encore après le bloc. Le compteur du FOR aussi, un pas après la fin.

IF 1 = 1
  found = 42
ENDIF
Console(Str(found))   ' 42
FOR i = 1 TO 3
NEXT
Console(Str(i))       ' 4

Les fonctions#

Dans une FUNCTION :

  • Les paramètres et les noms LOCAL appartiennent à l'appel.
  • Un nom GLOBAL, ou un nom que le programme affecte au-dessus de la fonction, est la variable du programme. La fonction la lit et l'écrit.
  • Tout autre nom affecté dans la fonction est une nouvelle variable locale.
score = 0
FUNCTION AddPoint()
  score = score + 1
ENDFUNCTION
AddPoint()
AddPoint()
Console(Str(score))   ' 2
FUNCTION Bump()
  LOCAL score = 99
  score = score + 1
ENDFUNCTION score
score = 1
Console(Str(Bump()) + " " + Str(score))   ' 100 1

⚠️ L'ordre des lignes compte. Si le programme n'affecte score qu'en dessous de la fonction, la fonction se fait son propre score, et le lire d'abord arrête le programme.

FUNCTION AddPoint()
  score = score + 1   ' une nouvelle locale : score n'est affecté que plus bas
ENDFUNCTION
score = 0
AddPoint()            ' s'arrête : Cannot access 'score' before initialization

⚠️ Avec DO … LOOP, les fonctions ne voient pas les variables ordinaires#

Dans un programme avec une boucle d'image, les lignes de préparation au-dessus de DO s'exécutent dans la fonction propre de la boucle. Une FUNCTION ne voit pas les variables qu'elles créent, même pour les lire seulement.

score = 10
FUNCTION Show()
  Console(Str(score))
ENDFUNCTION
DO
  Show()   ' s'arrête : score is not defined
  Sync()
LOOP

La solution est GLOBAL. Une variable GLOBAL est déclarée pour tout le programme, quelle que soit sa forme.

GLOBAL score = 10
FUNCTION Show()
  Console(Str(score))
ENDFUNCTION
DO
  Show()   ' 10, puis 11, 12…
  score = score + 1
  Sync()
LOOP

Les constantes ont la même limite : voir Constantes et opérateurs.

Déclarez avec GLOBAL toute valeur qu'une fonction partage avec le programme. Cela marche quelle que soit la forme du programme, et l'ordre des lignes cesse de compter.

@expose : un réglage dans l'Inspecteur#

' @expose [range(<min>, <max>)]
[GLOBAL] <name>[<sigil>] [AS <type>] = <value>

Un commentaire @expose sur la ligne juste au-dessus d'une affectation de premier niveau fait de cette variable un réglage. L'Inspecteur l'affiche, et la valeur qu'on y choisit remplace celle écrite dans le code au démarrage du programme.

' @expose range(0, 20)
speed# = 5.5
' @expose
title$ = "Héros"
' @expose
lives = 3
' @expose
godMode = FALSE
Console(title$ + " " + Str(speed#) + " " + Str(lives) + " " + Str(godMode))   ' Héros 5.5 3 0
La déclarationLe réglageLu avec
un nom $, ou AS stringtextePropString
un nom #, ou AS floatnombre décimalProp
TRUE ou FALSEmarche/arrêt, rangé en 1 ou 0PropInteger
tout le restenombre entierPropInteger
  • Le réglage porte le nom de la variable, sans son sigil : speed# devient speed.
  • range(a, b), ou simplement (a, b), fixe les bornes affichées dans l'Inspecteur.
  • Le marqueur peut s'écrire avec ', REM, // ou un accent grave.
  • Sous OPTION EXPLICIT, exposez une déclaration : GLOBAL lives = 3, ou speed AS float = 2.5.

⚠️ Un réglage décimal exige # ou AS float. zoom AS double = 1.5 et ratio = 0.75 deviennent des réglages entiers : ils partent de 1 et de 0.

⚠️ L'affectation doit être au premier niveau. Dans une FUNCTION ou un TYPE, le marqueur est refusé : @expose must be followed by a top-level assignment (name = value).

Vous venez d'un autre BASIC#

Vous attendezDans aukimi
x% = 2.7 range 2range 2.7 : utilisez x AS integer
float fait 64 bitsfloat fait 32 bits : utilisez double
Long fait 64 bits (Visual Basic)long fait 32 bits : utilisez quad
DIM x AS INTEGERx AS integer : DIM sans taille compile, mais le programme ne démarre pas
x.f (PureBasic)accepté
Define x = 1 au premier niveau (PureBasic)utilisez GLOBAL x = 1
une variable $ lue avant usage vaut ""elle vaut 0 : affectez-la d'abord
OPTION EXPLICIT contrôle tous les nomsil contrôle les noms où vous écrivez