Enregistrements et méthodes
Regrouper des champs dans des enregistrements TYPE, les copier comme des valeurs, atteindre leurs champs avec WITH, et leur donner des méthodes, un constructeur et un destructeur.
En un mot#
TYPE déclare un enregistrement : un groupe de champs nommés, chacun avec
son type. Un enregistrement est une valeur, comme un nombre : l'affecter à une
variable déclarée en fait une copie. METHOD ajoute des fonctions qui
appartiennent à l'enregistrement, avec SELF à l'intérieur.
TYPE Ship
name AS string
fuel AS integer
speed AS float
ENDTYPE
hero AS Ship
hero.name = "Comet"
hero.fuel = 80
hero.speed = 2.5
Console(hero.name + " " + Str(hero.fuel) + " " + Str(hero.speed)) ' Comet 80 2.5TYPE … ENDTYPE#
TYPE <Nom>
<champ>
…
[METHOD …]
ENDTYPE
| Forme du champ | Le champ contient |
|---|---|
name | un entier 32 bits |
name$ | une chaîne |
name# | un nombre décimal |
name AS <type> | ce type : integer, float, string, uint8… ou un autre type d'enregistrement |
name AS <type> [<dernier indice>, …] | un tableau de ce type |
name [<dernier indice>, …] | un tableau d'entiers |
LIST name.<type>() | une liste, voir Tableaux, listes et dictionnaires |
MAP name.<type>([<slots>]) | un dictionnaire |
ARRAY name.<type>(<dernier indice>, …) | un tableau, déclaré à la manière de PureBasic |
TYPE Player
name AS string
score
lives AS uint8
slots AS integer [3]
LIST items.s()
MAP counts.i()
ENDTYPE
p AS Player
p.name = "Ada"
p.score = 2.9
p.lives = 260
p.slots[3] = 7
AddElement(p.items())
p.items() = "Potion"
p.counts("Potion") = 3
Console(p.name + " " + Str(p.score) + " " + Str(p.lives) + " " + Str(p.slots[3])) ' Ada 2 4 7
Console(p.items() + " " + Str(p.counts("Potion"))) ' Potion 3
Un champ sans sigil ni
ASest un nombre entier.scoreci-dessus a rangé 2.9 comme 2. Les positions et les vitesses demandentAS float(32 bits) ouAS float64. AGK et DarkBASIC ont le même défaut.
- Un nouvel enregistrement commence avec 0 dans les nombres,
""dans les chaînes, et des collections vides. - Écrire un champ convertit la valeur dans le type du champ :
lives AS uint8a changé 260 en 4. - Les noms de champs ignorent la casse, et deux champs du même nom sont une erreur de compilation : « Duplicate BASIC field in Player: … ». Deux types du même nom sont refusés aussi.
- Un champ peut utiliser un type d'enregistrement déclaré plus loin dans le programme.
- Un type de champ inconnu est une erreur de compilation : « Unknown BASIC field type ».
- Le mot de fermeture est
ENDTYPE.END TYPE(QBasic, VB) est refusé :
TYPE Point
x
END TYPE
⚠️ Préférez AS aux sigils. Un champ écrit name$ ne garde son type que
tant que le même nom n'est jamais employé sans le $, nulle part dans le
programme. Une variable appelée name ailleurs suffit : le champ devient alors
un nombre entier, et y ranger du texte arrête le programme. name AS string n'a
pas ce problème.
⚠️ Évitez les noms de champ d'une lettre parmi b a w u l i q f d s c. Ces
lettres sont des suffixes de type PureBasic. Dans une méthode, SELF.w = 10 est
lu comme « déclarer une variable 16 bits », et le champ n'est jamais écrit.
Appelez-le width.
Le suffixe de champ de PureBasic (x.f) n'est pas accepté dans un TYPE. Écrivez
x AS float.
La forme sur une ligne#
TYPE Point x AS float y AS float ENDTYPE
TYPE Hero name AS string hp AS integer bag AS integer [2] ENDTYPE
p AS Point
p.x = 1.5
h AS Hero
h.bag[2] = 4
Console(Str(p.x) + " " + Str(h.bag[2])) ' 1.5 4
Le TYPE entier tient sur une ligne quand ses champs sont simples : un nom, un
AS <type> facultatif, des [bornes] facultatives. Les champs LIST, MAP et
ARRAY et les méthodes demandent la forme longue.
Des enregistrements dans des enregistrements#
Un champ peut être un enregistrement. Un TYPE peut même être déclaré à l'intérieur d'un autre ; son nom reste utilisable partout.
TYPE Vec
x AS float
y AS float
ENDTYPE
TYPE Ship
pos AS Vec
vel AS Vec
ENDTYPE
s AS Ship
s.pos.x = 3
s.vel.y = -1.5
Console(Str(s.pos.x) + " " + Str(s.vel.y)) ' 3 -1.5
Les champs tableaux#
Indexez un champ tableau avec une paire de crochets par dimension :
b.grid[1][2]. Les autres graphies compilent et tournent mal : b.grid(1, 2)
arrête le programme à l'exécution, et b.grid[1, 2] atteint sans un mot une
autre case.
TYPE Board
ARRAY grid.i(2, 2)
marks AS integer [4]
ENDTYPE
b AS Board
b.grid[1][2] = 5
b.marks[4] = 1
Console(Str(b.grid[1][2]) + " " + Str(b.marks.length)) ' 5 4
Les membres des tableaux (.length, .insert, .sort()…) marchent aussi sur
les champs tableaux. Voir
Tableaux, listes et dictionnaires.
Déclarer un enregistrement#
<nom> AS <Type>
<nom> AS <Type> = <enregistrement>
<nom> AS <Type> = NEW <Type>(<arguments>)
<nom> AS <Type>[<dernier indice>]
DIM <nom>(<dernier indice>) AS <Type>
NewList <nom>.<Type>()
NewMap <nom>.<Type>()
- Une variable enregistrement doit être déclarée avec
AS. Écrireq.x = 1sur un nom jamais déclaré compile, puis arrête le programme : « q is not defined ». - Les noms de types ignorent la casse :
hero AS shipconvient. - Chaque déclaration crée un enregistrement neuf et exécute sa méthode
New, s'il en a une. De même pour chaque case d'un tableau d'enregistrements, chaqueAddElementsur une liste d'enregistrements, chaque nouvelle entrée de dictionnaire, et chaque champ enregistrement d'un nouvel enregistrement.
Atteindre un champ#
| Où est l'enregistrement | Graphie |
|---|---|
| une variable | hero.fuel |
| un enregistrement dans un enregistrement | s.pos.x |
| une case de tableau | fleet(2).fuel ou fleet[2].fuel |
| l'élément courant d'une liste | foes().hp |
| une entrée de dictionnaire | bag("sword").qty |
La graphie avec antislash hero\fuel appartient aux blocs WITH. En dehors, elle
est refusée avec « Not a BASIC statement ».
⚠️ Une écriture par un élément de liste ou une entrée de dictionnaire n'est pas
convertie dans le type du champ : foes().hp = 3.7 garde 3.7 dans un champ
entier. Les écritures par une variable ou une case de tableau sont converties.
Les enregistrements sont des valeurs#
TYPE Vec
x AS float
ENDTYPE
TYPE Ship
pos AS Vec
name AS string
ENDTYPE
a AS Ship
a.pos.x = 3
b AS Ship
b = a
b.pos.x = 9
Console(Str(a.pos.x) + " " + Str(b.pos.x)) ' 3 9
Affecter un enregistrement à une variable déclarée en fait une copie profonde. Les enregistrements, tableaux, listes et dictionnaires imbriqués sont copiés avec lui. Les deux enregistrements sont ensuite indépendants.
La même copie a lieu pour x AS T = autre, pour un élément de liste
(l() = rec) et pour une entrée de dictionnaire (m("k") = rec). Passer un
enregistrement à une FUNCTION le copie aussi, sauf si le paramètre est REF,
voir Les fonctions.
⚠️ Trois affectations ne copient pas, et partagent à la place :
| Affectation | Résultat |
|---|---|
fleet(1) = hero, une case de tableau | la case et hero sont le même enregistrement |
s.pos = v, un champ enregistrement | le champ et v sont le même enregistrement |
w = hero, avec w jamais déclaré AS Ship | w et hero sont le même enregistrement |
Après l'une d'elles, un changement par un nom se voit par l'autre. Pour ranger une copie indépendante dans une case de tableau ou un champ, copiez les champs un par un :
TYPE Vec
x AS float
y AS float
ENDTYPE
DIM path(1) AS Vec
v AS Vec
v.x = 1
v.y = 2
path(0).x = v.x
path(0).y = v.y
v.x = 7
Console(Str(path(0).x)) ' 1, la case a gardé sa propre valeur
Comparer des enregistrements. a = b sur deux enregistrements demande s'il
s'agit du même enregistrement, pas si leurs champs sont égaux. Deux
enregistrements distincts, même aux champs égaux, ne sont jamais =. Comparez
les champs qui vous intéressent.
WITH#
WITH <enregistrement>
\<champ> …
ENDWITH
TYPE Vec
x AS float
y AS float
ENDTYPE
TYPE Ship
pos AS Vec
name AS string
fuel AS integer
ENDTYPE
s AS Ship
WITH s
\name = "Comet"
\fuel = 80
\pos\x = 1.5
\pos.y = 2.5
Console(\name + " " + Str(\fuel) + " " + Str(\pos\x) + " " + Str(\pos\y)) ' Comet 80 1.5 2.5
ENDWITH
- Dans le bloc,
\champsignifie<enregistrement>.champ, n'importe où sur la ligne. - Une chaîne s'écrit
\pos\xou\pos.x. - Le bloc se ferme par
ENDWITHouEND WITH. - L'enregistrement peut être n'importe quelle expression :
WITH fleet(i),WITH foes(). - Le texte des chaînes et des commentaires n'est pas touché :
"C:\games"reste un chemin. - Les blocs WITH ne s'imbriquent pas. Un WITH dans un WITH est une erreur de compilation : « Nested WITH blocks are not supported ».
⚠️ L'expression de l'enregistrement est relue à chaque \. Le compilateur
écrit l'expression devant chaque champ. Avec WITH fleet(i), changer i dans
le bloc envoie les lignes suivantes vers une autre case.
WITH hero n'est pas WITH SPRITE s. Un seul mot après WITH fait un bloc
d'enregistrement. Deux mots, dont le premier nomme une famille de commandes, font
le raccourci de commandes : \position 100, 200 appelle alors
SpritePosition(s, 100, 200). Voir Les raccourcis.
METHOD#
TYPE <Nom>
<champs>
METHOD <méthode>([<paramètres>])
<instructions>
[EXITMETHOD [<valeur>]]
ENDMETHOD
ENDTYPE
TYPE Counter
value AS integer
METHOD Add(amount AS integer)
SELF.value = SELF.value + amount
EXITMETHOD SELF.value
ENDMETHOD
METHOD Reset()
SELF.value = 0
ENDMETHOD
ENDTYPE
c AS Counter
Console(Str(c.Add(5))) ' 5
Console(Str(c.Add(3))) ' 8
c.Reset()
Console(Str(c.value)) ' 0
- Une méthode se déclare dans son TYPE, parmi les champs.
SELFest l'enregistrement sur lequel la méthode a été appelée. ÉcrireSELF.valuemodifie cet enregistrement, y compris quand c'est une case de tableau ou un élément de liste :counters(2).Reset(),counters().Add(3).- On l'appelle par
enregistrement.méthode(arguments), comme instruction ou dans une expression. Les noms de méthodes ignorent la casse. EXITMETHODquitte la méthode.EXITMETHOD <valeur>rend une valeur.RETURN <valeur>fait la même chose.- Les parenthèses sont obligatoires, même vides :
METHOD Reset(). - Une méthode est une FUNCTION en dessous : paramètres typés, valeurs par
défaut, variables
LOCALetOPTIONALmarchent tous. Voir Les fonctions. - Une méthode peut en appeler une autre par
SELF:SELF.Reset(). - La copie d'un enregistrement a les mêmes méthodes que l'original.
- Dans un programme qui a une boucle DO, une variable du programme qu'une
méthode utilise doit être déclarée
GLOBAL, comme pour toute FUNCTION.
Refusés à la compilation : une METHOD hors d'un TYPE (« METHOD requires a
containing TYPE »), une METHOD dans une METHOD, deux méthodes du même nom dans un
TYPE, et une valeur après ENDMETHOD. Contrairement à ENDFUNCTION,
ENDMETHOD ne prend pas de valeur, et END METHOD n'est pas une graphie :
TYPE Counter
value AS integer
METHOD Get()
ENDMETHOD SELF.value
ENDTYPE
⚠️ N'appelez pas une méthode Length, Find, Sort, Insert ou Remove.
Ces noms appartiennent aux membres des tableaux, et l'appel arrête le programme
à l'exécution.
New : le constructeur#
Une méthode nommée New s'exécute chaque fois qu'un enregistrement de ce type
est créé.
TYPE Counter
value AS integer
METHOD New(start AS integer = 0)
SELF.value = start
ENDMETHOD
ENDTYPE
a AS Counter
b AS Counter = NEW Counter(7)
Console(Str(a.value) + " " + Str(b.value)) ' 0 7
NEW <Type>(<arguments>)crée un enregistrement et passe les arguments àNew. Cela marche dans une déclaration et dans une affectation ultérieure :b = NEW Counter(2).- Toutes les autres créations appellent
Newsans argument :x AS T, chaque case deDIM a(n) AS T,AddElementsur une liste d'enregistrements, une nouvelle entrée de dictionnaire, un champ enregistrement de ce type. - Une copie n'exécute pas
New:b = aetx AS T = autrecopient les champs tels quels.
⚠️ Donnez une valeur par défaut à chaque paramètre de New. Comme la
plupart des créations ne passent pas d'argument, un paramètre typé sans valeur
par défaut arrête le programme (« BASIC integer requires an integral value »),
et un paramètre non typé laisse le champ sans valeur.
DELETE et la méthode Delete#
DELETE <variable enregistrement>
DELETE <tableau>[<indice>]
DELETE <enregistrement>.<champ>
alive = 0
TYPE Enemy
hp AS integer
METHOD New()
alive = alive + 1
ENDMETHOD
METHOD Delete()
alive = alive - 1
ENDMETHOD
ENDTYPE
e AS Enemy
f AS Enemy
Console(Str(alive)) ' 2
DELETE e
Console(Str(alive)) ' 1
DELETEappelle la méthodeDeletede l'enregistrement, s'il en a une, puis marque l'enregistrement comme supprimé.- Sur une variable,
DELETEla vide aussi. Lire un de ses champs ensuite arrête le programme. - Sur une case de tableau ou un champ, la case reste, mais appeler une de ses
méthodes arrête le programme : « BASIC record has been deleted ». Le chemin
prend des crochets :
DELETE fleet[2].DELETE fleet(2)est refusé. Deletene s'exécute que parDELETE. Réaffecter une variable, ou atteindre la fin du programme, ne l'appelle pas.
Le Structure de PureBasic#
Structure … EndStructure est accepté comme une autre graphie de
TYPE … ENDTYPE, forme sur une ligne comprise. Voir
Les raccourcis.
Structure Size width height EndStructure
s AS Size
s.width = 2
Console(Str(s.width)) ' 2Si vous venez d'un autre BASIC#
| Vous écrivez peut-être | Dans aukimi |
|---|---|
Type … End Type | TYPE … ENDTYPE |
x.f dans une Structure (PureBasic) | x AS float |
p\x hors d'un With (PureBasic) | p.x |
AllocateStructure (PureBasic) | p AS Point, ou NEW Point() |
FreeStructure (PureBasic) | DELETE p |
END METHOD, ENDMETHOD valeur | ENDMETHOD, précédé de EXITMETHOD valeur |
With imbriqués | un seul WITH à la fois |