Tableaux, listes et dictionnaires
Ranger de nombreuses valeurs dans des tableaux, des listes et des dictionnaires, les parcourir avec FOR et FOREACH, et lire des tables constantes avec DATA et READ.
En un mot#
aukimi a trois conteneurs pour de nombreuses valeurs, plus une façon d'écrire des tables constantes dans le programme lui-même :
| Tableau | Liste | Dictionnaire | |
|---|---|---|---|
| Déclarer | DIM a(10) ou a AS integer[] | NewList l.i() | NewMap m.i() |
| Trouver une valeur par | sa position : a(3) | l'élément courant : l() | une clé texte : m("gold") |
| Agrandir | .insert(v), .length = n | AddElement(l()) | écrire une nouvelle clé |
| Parcourir | FOR i = 0 TO a.length | FOREACH l() | FOREACH m() |
Affecter un conteneur à une autre variable ne le copie pas. Après
b = a, les deux noms désignent le même tableau, la même liste ou le même dictionnaire, et un changement par l'un se voit par l'autre. Copiez avecCopyArray,CopyListouCopyMap.
Les enregistrements, qui regroupent des champs nommés, ont leur propre page : Enregistrements et méthodes.
DIM#
DIM <nom>(<dernier indice> [, <dernier indice>…]) [AS <type>] [= [<valeur>, …]]
DIM <nom>[<dernier indice> [, <dernier indice>…]] [AS <type>] [= [<valeur>, …]]
DIM scores(4)
scores(0) = 10
scores(4) = 50
Console(Str(scores.length)) ' 4, le dernier indice
FOR i = 0 TO scores.length
Console(Str(scores(i))) ' 10, 0, 0, 0, 50
NEXT
Le nombre est le dernier indice, pas le nombre de cases.
DIM scores(4)crée cinq cases, numérotées de 0 à 4. C'est la règle d'AGK, de DarkBASIC et de PureBasic, pas celle du C ni du JavaScript.
- Chaque case commence à 0, ou à
""quand le nom finit par$. - Une borne peut être n'importe quelle expression :
DIM grid(width, height). - Avec
AS <type>, chaque écriture est convertie dans ce type.AS integersupprime les décimales,AS uint8repart de zéro à 256. AvecASsuivi d'un type d'enregistrement, chaque case est un enregistrement. Voir Variables et types. - Un
DIMsur un nom qui existe déjà crée un nouveau tableau : les anciennes valeurs sont perdues. Pour redimensionner en gardant les valeurs, affectez.length(plus bas). - Un
DIMen haut du programme est visible dans toutes les FUNCTION. UnDIMdans une FUNCTION appartient à cette fonction.
DIM names$(2)
names$(1) = "Ada"
DIM ages(2) AS integer
ages(0) = 3.7
DIM bytes(1) AS uint8
bytes(0) = 300
Console(names$(1) + " " + Str(ages(0)) + " " + Str(bytes(0))) ' Ada 3 44
⚠️ GLOBAL DIM et LOCAL DIM ne sont pas fiables. Avec des parenthèses,
l'usage suivant du tableau est refusé comme une commande inconnue. Dans une
FUNCTION, GLOBAL DIM ne rend pas le tableau visible au-dehors. Écrivez plutôt
un DIM simple en haut du programme.
Plusieurs dimensions, trois graphies#
DIM grid(2, 3)
grid(1, 2) = 7
Console(Str(grid[1, 2])) ' 7
Console(Str(grid[1][2])) ' 7
Console(Str(grid.length)) ' 2, dernier indice de la première dimension
Console(Str(grid[0].length)) ' 3, dernier indice de la seconde dimension
a(i, j), a[i, j] et a[i][j] lisent et écrivent la même case. Les
parenthèses sont la graphie classique, les crochets celle d'AGK.
⚠️ Il n'y a pas de contrôle des bornes. Lire au-delà du dernier indice ne
donne aucune valeur (Str affiche undefined). Écrire au-delà agrandit le
tableau sans prévenir, et les cases intermédiaires n'ont pas de valeur non plus.
Comparez votre indice à .length.
Les tableaux typés : AS <type>[]#
<nom> AS <type>[]
<nom> AS <type>[<dernier indice> [, <dernier indice>…]]
values AS integer[] = [1, 2, 3]
Console(Str(values.length)) ' 2
more AS integer[5] = [9]
Console(Str(more[0]) + " " + Str(more[5])) ' 9 0
empty AS string[]
Console(Str(empty.length)) ' -1, aucune case
C'est le même tableau que DIM … AS, écrit à la manière d'AGK. Avec [] sans
borne, le tableau commence vide. Les paramètres et les résultats de FUNCTION
utilisent aussi cette forme, voir Les fonctions.
Les initialiseurs et l'affectation d'une liste#
= [ … ] remplit les cases à partir de l'indice 0. Cela ne marche que sur un
tableau à une dimension. Une liste plus longue que le tableau l'agrandit.
Plus loin dans le programme, affecter une liste écrit à partir de l'indice 0 et garde les autres cases. Le tableau grandit si besoin, et ne rétrécit jamais :
DIM a(4)
a = [7, 8]
Console(Str(a(0)) + Str(a(1)) + " " + Str(a.length)) ' 78 4Les membres des tableaux#
DIM a(2) = [30, 10, 20]
a.insert(5)
a.insert(99, 0)
Console(Str(a.length)) ' 4
a.remove()
a.remove(0)
a.sort()
Console(Str(a(0)) + " " + Str(a(1)) + " " + Str(a(2))) ' 10 20 30
Console(Str(a.find(20))) ' 1
Console(Str(a.find(42))) ' -1
| Membre | Ce qu'il fait |
|---|---|
a.length | le dernier indice : le nombre de cases moins un, -1 quand le tableau est vide |
a.length = n | redimensionne pour que n soit le dernier indice ; les cases gardées gardent leur valeur, les nouvelles reçoivent la valeur par défaut |
a.insert(v) | ajoute v à la fin |
a.insert(v, i) | insère v à l'indice i et décale la suite |
a.remove() | retire la dernière case |
a.remove(i) | retire la case i et décale la suite |
a.find(v) | l'indice de la première case égale à v, ou -1 |
a.sort() | trie sur place, du plus petit au plus grand |
UNDIM a[] | vide le tableau (.length passe à -1) |
.lengthest le dernier indice. Une boucle sur toutes les cases s'écritFOR i = 0 TO a.length, et le nombre de cases vauta.length + 1. C'est la règle d'AGK. Pour qui vient du JavaScript, c'est le piège.
.insert,.removeet.length = nsont des instructions, seules sur leur ligne. Elles ne rendent pas de valeur..findutilise l'égalité stricte :a.find("2")ne trouve pas le nombre 2. Il parcourt tout le tableau, qui n'a pas besoin d'être trié..sort()compare les chaînes caractère par caractère, selon leur code : toutes les majuscules passent avant toutes les minuscules ("Zebra"avant"apple"). Un tableau d'enregistrements est trié sur son premier champ.- Les membres marchent sur les tableaux rangés dans un enregistrement :
bag.names.insert("Key"). UNDIMprend des crochets.UNDIM a()compile en un programme cassé :
DIM a(2)
UNDIM a()
Les commandes du catalogue qui prennent un tableau. SortArray(a, options)
trie avec des options (#AK_ORDER_HIGH_TO_LOW pour le plus grand d'abord),
RandomizeArray(a) mélange, CopyArray(source, destination) copie,
CompareArray(a, b) rend 1 quand les deux contiennent les mêmes valeurs, et
ArraySize(a) rend le dernier indice comme .length. Voir
la référence des commandes.
DIM a(2) = [1, 2, 3]
DIM b(0)
CopyArray(a, b)
b(0) = 99
Console(Str(a(0)) + " " + Str(b.length)) ' 1 2, a est intact, b a été redimensionnéLes listes#
NewList <nom>.<type>()
NewList <nom>()
Une liste range des valeurs dans l'ordre et a un seul élément courant. La plupart des opérations déplacent ce curseur, puis lisent ou écrivent l'élément qu'il désigne.
NewList scores.i()
AddElement(scores())
scores() = 10
AddElement(scores())
scores() = 30
FirstElement(scores())
AddElement(scores())
scores() = 20
FOREACH scores()
Console(Str(ListIndex(scores())) + ": " + Str(scores())) ' 0: 10, 1: 20, 2: 30
NEXT scores()
Console(Str(ListSize(scores()))) ' 3
scores()est l'élément courant. On le lit, ou on l'affecte :scores() = 10. Sans élément courant, le programme s'arrête sur « BASIC list has no current element ».AddElementinsère après l'élément courant et rend le nouveau courant. C'est pourquoi 20 arrive entre 10 et 30 ci-dessus.- Le type suit le point : un suffixe court (
.i,.f,.d,.s,.q…), un nom complet (.integer,.string,.float64), ou un type d'enregistrement (NewList foes.Foe(), voir plus bas). Sans type, la liste contient des entiers. - Les valeurs sont converties dans le type de la liste.
.fest un flottant 32 bits :0.1se relit0.10000000149011612. Prenez.dpour la pleine précision. - Une liste d'enregistrements donne accès aux champs par l'élément courant :
foes().hp = 30. - Un même nom ne peut pas être à la fois une liste et un dictionnaire.
⚠️ Le sigil $ ne déclare pas une liste. NewList names$() est refusé.
Écrivez le type après un point :
NewList names$()
NewList names.s()
AddElement(names())
names() = "Ada"
Console(names()) ' Ada
⚠️ += sur l'élément courant échoue à l'exécution, tout comme
INC scores(). Écrivez scores() = scores() + 5.
FOREACH#
FOREACH <liste>()
<instructions>
NEXT [<liste>()]
FOREACH parcourt une liste du premier élément au dernier. Dans la boucle,
liste() est l'élément du passage en cours, et on peut le modifier.
NewList a.i()
FOR k = 1 TO 5
AddElement(a())
a() = k
NEXT
FOREACH a()
IF Mod(a(), 2) = 0 THEN DeleteElement(a())
NEXT
FOREACH a()
Console(Str(a())) ' 1, 3, 5
NEXT
- Supprimer l'élément courant dans un FOREACH est sans danger : la boucle continue avec le suivant.
EXITetCONTINUEmarchent comme dans toute boucle.- Les parenthèses sont obligatoires :
FOREACH aetNEXT asont des erreurs de compilation.NEXTseul convient. - FOREACH marche sur les dictionnaires (plus bas) et sur les champs liste ou
dictionnaire d'un enregistrement :
FOREACH bag.items(). - FOREACH ne marche pas sur les tableaux : utilisez
FOR i = 0 TO a.length. - FOREACH déplace l'unique curseur de la liste. Un FOREACH imbriqué sur la même liste, ou un appel qui déplace le curseur, change l'endroit où en est la boucle extérieure.
- Le
ForEach … Nextde PureBasic est la même chose : les mots-clés ignorent la casse.
Les 25 appels de liste#
Chaque appel prend la liste avec ses parenthèses, scores(). Les positions
comptent à partir de 0.
| Appel | Ce qu'il fait | Rend |
|---|---|---|
AddElement(l()) | ajoute un élément après le courant (en première place s'il n'y en a pas) et le rend courant | 1 |
InsertElement(l()) | ajoute un élément avant le courant et le rend courant | 1 |
DeleteElement(l() [, flags]) | supprime l'élément courant ; le précédent devient courant. Avec flags = 1, supprimer le premier élément rend courant le nouveau premier | 1 s'il reste un élément courant, sinon 0 |
ClearList(l()) | retire tous les éléments | rien |
FreeList(l()) | retire tous les éléments et libère la liste ; ListSize rend alors -1 | rien |
ListSize(l()) | le nombre d'éléments | un nombre, -1 après FreeList |
ListIndex(l()) | la position de l'élément courant | une position, -1 s'il n'y en a pas |
ResetList(l()) | place le curseur avant le premier élément | rien |
NextElement(l()) | passe à l'élément suivant | 1, ou 0 après la fin |
PreviousElement(l()) | passe à l'élément précédent | 1, ou 0 avant le début |
FirstElement(l()) | va au premier élément | 1, ou 0 si la liste est vide |
LastElement(l()) | va au dernier élément | 1, ou 0 si la liste est vide |
SelectElement(l(), i) | va à la position i | 1, ou 0 hors limites |
ChangeCurrentElement(l(), i) | la même chose que SelectElement | 1 ou 0 |
CopyList(source(), destination()) | remplace la destination par une copie de la source | 1, ou 0 si les types diffèrent |
CompareList(a(), b() [, flags]) | compare deux listes élément par élément ; #AK_MATCH_FOLD_CASE ignore la casse | 1 si égales, sinon 0 |
MergeLists(source(), destination() [, where]) | déplace tous les éléments de la source dans la destination ; where vaut #AK_LIST_LAST (par défaut), #AK_LIST_FIRST, ou #AK_LIST_BEFORE / #AK_LIST_AFTER l'élément courant de la destination | rien |
SplitList(source(), destination() [, keep]) | déplace l'élément courant et tout ce qui le suit dans la destination ; avec keep = 1, l'élément courant reste | rien |
SortList(l(), options [, start, end]) | trie une liste de nombres ou de chaînes ; options #AK_ORDER_LOW_TO_HIGH, #AK_ORDER_HIGH_TO_LOW, #AK_ORDER_FOLD_CASE | rien |
SortStructuredList(l(), "field", options [, start, end]) | trie une liste d'enregistrements sur un champ, nommé en texte | rien |
RandomizeList(l() [, start, end]) | mélange la liste, ou la plage | rien |
PushListPosition(l()) | mémorise l'élément courant | rien |
PopListPosition(l()) | revient à l'élément mémorisé | rien |
MoveElement(l(), where [, i]) | déplace l'élément courant : #AK_LIST_FIRST, #AK_LIST_LAST, ou #AK_LIST_BEFORE / #AK_LIST_AFTER l'élément à la position i | rien |
SwapElements(l(), i, j) | échange les éléments aux positions i et j | rien |
Un mauvais nombre d'arguments est une erreur de compilation, par exemple
« SortList(...) expects 2-4 argument(s) ». Les options de tri sont
obligatoires : SortList(l()) seul est refusé.
TYPE Foe
name AS string
hp AS integer
ENDTYPE
NewList foes.Foe()
AddElement(foes())
foes().name = "orc"
foes().hp = 30
AddElement(foes())
foes().name = "elf"
foes().hp = 10
SortStructuredList(foes(), "hp", #AK_ORDER_LOW_TO_HIGH)
FOREACH foes()
Console(foes().name + " " + Str(foes().hp)) ' elf 10, orc 30
NEXT
Si vous venez de PureBasic. Les noms et la plupart des arguments
correspondent. Trois appels prennent une position là où PureBasic prend un
pointeur d'élément, parce qu'aukimi n'a pas de pointeurs d'élément :
SwapElements, ChangeCurrentElement et le dernier argument de MoveElement.
SortStructuredList prend le nom du champ en texte, pas un décalage et un
type. AddElement rend 1, pas l'adresse de l'élément. foes()\hp s'écrit
foes().hp.
Les dictionnaires#
NewMap <nom>.<type>([<slots>])
Un dictionnaire range des valeurs sous des clés texte. Il a lui aussi un élément courant.
NewMap stock.i()
stock("apple") = 3
stock("pear") = 5
stock("apple") += 2
Console(Str(stock("apple"))) ' 5
Console(Str(MapSize(stock()))) ' 2
IF FindMapElement(stock(), "plum") = 0 THEN Console("no plum")
FOREACH stock()
Console(MapKey(stock()) + " = " + Str(stock())) ' apple = 5, pear = 5
NEXT
m("clé") = vcrée l'entrée ou remplace sa valeur.+=et les autres affectations composées marchent sur une entrée.- Lire
m("clé")crée l'entrée quand elle manque, avec la valeur par défaut. Pour tester sans créer, utilisezFindMapElement. - Chaque accès rend cette entrée courante.
m()est l'entrée courante : on la lit, ou on l'affecte. - Les clés sont du texte. Un nombre est converti en texte :
m(1)etm("1")sont la même entrée. Les clés distinguent la casse :"A"et"a"sont deux entrées. - FOREACH et
NextMapElementvisitent les entrées dans leur ordre de création. <slots>est une indication de taille gardée pour les programmes PureBasic. Elle doit être un entier positif et ne change rien d'autre.- Un dictionnaire d'enregistrements donne accès aux champs par l'entrée :
bag("sword").qty = 1.
Les 13 appels de dictionnaire#
| Appel | Ce qu'il fait | Rend |
|---|---|---|
AddMapElement(m(), key [, flags]) | crée l'entrée, ou remet une entrée existante à la valeur par défaut, et la rend courante. flags doit valoir 0 | 1 |
FindMapElement(m(), key) | rend l'entrée courante si elle existe. Ne crée rien | 1 si trouvée, sinon 0 |
DeleteMapElement(m() [, key]) | supprime l'entrée, ou l'entrée courante | 1 si quelque chose a été supprimé, sinon 0 |
ClearMap(m()) | retire toutes les entrées | rien |
FreeMap(m()) | retire toutes les entrées et libère le dictionnaire ; MapSize rend alors -1 | rien |
MapSize(m()) | le nombre d'entrées | un nombre, -1 après FreeMap |
MapKey(m()) | la clé de l'entrée courante | du texte, "" s'il n'y en a pas |
ResetMap(m()) | place le curseur avant la première entrée | rien |
NextMapElement(m()) | passe à l'entrée suivante | 1, ou 0 après la fin |
CopyMap(source(), destination()) | remplace la destination par une copie de la source | 1, ou 0 si les types diffèrent |
CompareMap(a(), b() [, flags]) | compare clés et valeurs ; #AK_MATCH_FOLD_CASE ignore la casse | 1 si égaux, sinon 0 |
PushMapPosition(m()) | mémorise l'entrée courante | rien |
PopMapPosition(m()) | revient à l'entrée mémorisée | rien |
NewMap m.s()
m("x") = "1"
m("y") = "2"
ResetMap(m())
WHILE NextMapElement(m())
Console(MapKey(m()) + "=" + m()) ' x=1, y=2
WENDINC et DEC#
INC <cible> [, <quantité>]
DEC <cible> [, <quantité>]
n = 5
INC n
INC n, 10
DEC n, 3
Console(Str(n)) ' 13
DIM a(2)
INC a[1], 4
Console(Str(a[1])) ' 4
b AS uint8 = 255
INC b
Console(Str(b)) ' 0, un uint8 repart de zéro
INCajoute 1, ou la quantité.DECretranche.- La cible est une variable, une case de tableau entre crochets
a[i], un champp.x, ou un mélange :ps[i].x. - Sur une variable typée, le résultat boucle comme toute écriture dans ce type.
- Sous
OPTION EXPLICIT, la cible doit d'abord être déclarée.
⚠️ Utilisez des crochets pour une case de tableau. INC a(1) compile en un
programme cassé, tout comme INC ps(1).x, INC l() et INC m("k"). Écrivez
INC a[1], INC ps[1].x, l() = l() + 1, m("k") += 1.
DIM a(2)
INC a(1)Échanger deux valeurs#
Il n'y a pas d'instruction SWAP a, b. La ligne est refusée :
a = 1
b = 2
SWAP a, b
Swap() avec des parenthèses existe, mais fait autre chose : il termine une
image, comme Sync(). Voir La boucle de jeu et les tâches.
Échangez deux variables ou deux cases de tableau par une troisième. Échangez deux
éléments de liste avec SwapElements(l(), i, j).
a = 1
b = 2
temp = a
a = b
b = temp
Console(Str(a) + " " + Str(b)) ' 2 1DATA, READ et RESTORE#
DATASECTION
[<étiquette>:]
DATA[.<type>] <littéral> [, <littéral>…]
ENDDATASECTION
READ[.<type>] <cible>
RESTORE [<étiquette>]
Une DATASECTION contient des valeurs constantes écrites dans le programme.
READ les prend une à une, dans l'ordre. RESTORE déplace la position de
lecture.
RESTORE Levels
READ count
FOR i = 1 TO count
READ name$
READ.f speed#
Console(name$ + " " + Str(speed#)) ' Forest 1.5, Cave 2.25
NEXT
DATASECTION
Levels:
DATA 2
DATA.s "Forest"
DATA.f 1.5
DATA.s "Cave"
DATA.f 2.25
ENDDATASECTION
| Partie | Graphies acceptées |
|---|---|
| Ouverture | DATASECTION ou DATA SECTION |
| Fermeture | ENDDATASECTION, END DATASECTION ou END DATA SECTION |
Les types de DATA. Chaque ligne DATA a un seul type, écrit après un point :
| Suffixe | Contient |
|---|---|
aucun, .i (aussi .l, .q, .w, .b, .a, .u, .c) | des nombres |
.f, .d | des nombres |
.s | des chaînes entre guillemets doubles |
DATAn'est permis que dans uneDATASECTION. La section n'est pas exécutée : elle peut se trouver en haut ou en bas du programme.- Les valeurs doivent être des littéraux. Les nombres peuvent être négatifs,
décimaux,
%1010,0c12,0x10ou1.25e2.TRUEetFALSEcomptent pour 1 et 0.DATA 1 + 2est une erreur de compilation, tout comme une chaîne dans une ligneDATAsans.s. - Les valeurs sont lues dans l'ordre d'écriture, d'une ligne et d'une étiquette à l'autre.
READ. Le genre à lire vient de READ.<type>, ou à défaut du sigil de la
cible : $ lit une chaîne, # un nombre décimal, sans sigil un nombre entier.
- La cible peut être une variable, une case de tableau (
a(1)oua[1]), ou un champ (p.x). - Lire un entier dans une valeur décimale supprime les décimales : 2.9 donne 2.
- Lire une chaîne quand la valeur suivante est un nombre, ou l'inverse, arrête le programme : « BASIC READ type does not match DATA ».
- Lire au-delà de la dernière valeur arrête le programme : « BASIC READ reached the end of DATA ».
RESTORE. RESTORE seul revient à la première valeur. RESTORE Étiquette va
à la première valeur qui suit cette étiquette. Les étiquettes ignorent la casse,
et une étiquette inconnue est une erreur de compilation. Une étiquette de données
n'est pas une étiquette de GOSUB : on ne peut pas l'appeler.
Si vous venez d'un autre BASIC. AGK n'a pas de DATA. DarkBASIC lit des lignes
DATA non typées, sans section. aukimi suit la DataSection de PureBasic, avec
une ligne DATA typée, et ajoute deux raccourcis : un DATA sans type contient
des nombres, et READ peut prendre son type dans le sigil.