
table
Listes et tables : ajouter, retirer, trier, joindre, copier et chercher.
Une table range des valeurs sous des clés. On s'en sert de deux façons, souvent mélangées dans la même table :
- une liste : des valeurs aux positions 1, 2, 3...
{"fer", "or", "cuivre"}. Sa longueur est#t. - un enregistrement : des valeurs sous des noms.
{nom = "Lingot de fer", nombre = 64}, lues avect.nomout["nom"].
La bibliothèque table travaille surtout sur les listes : table.insert, table.remove, table.sort et table.concat ne regardent que les positions 1 à #t. Le guide Tables explique les tables depuis le début.
local commandes = {"fer", "or"}
table.insert(commandes, "cuivre")
table.insert(commandes, 1, "charbon")
print(table.concat(commandes, ", "))
local premiere = table.remove(commandes, 1)
print(premiere, #commandes)
table.sort(commandes)
print(table.concat(commandes, " "))charbon, fer, or, cuivre charbon 3 cuivre fer or
Une table se partage, elle ne se copie pas. local b = a donne un deuxième nom à la même table : un changement fait par b se voit par a. Utilisez table.copy pour obtenir une table séparée. De même, == dit si deux noms désignent la même table, pas si deux tables contiennent la même chose.
**La longueur #t compte la partie liste : les positions 1, 2, 3... remplies l'une après l'autre. Une table qui n'a que des clés nommées a une longueur de 0. Mettre la dernière** valeur à nil raccourcit la liste ; en mettre une au milieu à nil laisse un trou que # compte toujours, mais où ipairs s'arrête. Une valeur loin après la fin (t[10] dans une liste de 3) ne fait pas partie de la liste tant que le vide n'est pas comblé.
local t = {"a", "b", "c"}
t[2] = nil
print(#t)
t[3] = nil
print(#t)
t[10] = "j"
print(#t)3 1 1
Gardez vos listes sans trous : retirez les valeurs avec table.remove, qui referme l'espace, plutôt qu'en les mettant à nil.
Chaque entrée d'une table prend 2 cellules de mémoire, plus 4 pour la table elle-même (voir Limites). Brass n'a ni table.unpack, ni table.pack, ni table.move, ni métatables.
table.insert(t, [pos,] value) | Ajoute une valeur à la fin d'une liste, ou à une position donnée. |
table.remove(t [, pos]) | Retire une valeur d'une liste et la renvoie : la dernière, ou celle de la position pos. |
table.sort(t [, less]) | Trie une liste sur place : le plus petit d'abord, ou dans l'ordre donné par une fonction. |
table.concat(t [, sep [, i [, j]]]) | Joint les valeurs d'une liste en un seul texte, avec sep entre elles. |
table.contains(t, value) | Vrai si la valeur se trouve quelque part dans la table. |
table.keys(t) | La liste des clés d'une table. |
table.copy(t) | Une copie simple d'une table : une nouvelle table avec les mêmes clés et valeurs. |
Ajouter et retirer
Ajoute une valeur à la fin d'une liste, ou à une position donnée.
ttable- la liste
posnumber facultatif- la position où va la valeur, de 1 à
#t + 1(la fin si elle est omise) valueany- la valeur à ajouter
Avec deux arguments, la valeur va à la fin, en position #t + 1. Avec trois, elle va en pos, et les valeurs à partir de pos remontent d'une place. Une file de commandes de trains, avec une commande urgente qui passe devant :
local attente = {}
table.insert(attente, "Fer vers le dépôt A")
table.insert(attente, "Charbon vers la fonderie")
table.insert(attente, 1, "URGENT : vivres pour la base")
for i, commande in ipairs(attente) do
print(i, commande)
end1 URGENT : vivres pour la base 2 Fer vers le dépôt A 3 Charbon vers la fonderie
posdoit être un entier de 1 à#t + 1. Sinon le programme s'arrête avecbad argument #2 to 'insert' (position out of bounds).- Une valeur
niln'ajoute rien : une liste ne peut pas contenirnil, sa longueur ne change pas. - Avec seulement la table, le programme s'arrête avec
wrong number of arguments to 'insert'. t[#t + 1] = valeurfait la même chose quetable.insert(t, valeur), sans le coût d'un appel.
Ajouter à la fin ne coûte presque rien. Insérer à une position déplace les valeurs qui suivent, ce qui coûte une instruction par tranche de 8 valeurs de la liste : sur une liste de milliers de valeurs, préférez ajouter à la fin.
Voir aussi table.remove()
Retire une valeur d'une liste et la renvoie : la dernière, ou celle de la position pos.
ttable- la liste
posnumber facultatif- la position à retirer, de 1 à
#t(la dernière si elle est omise)
- any
- la valeur retirée, nil si la liste était vide
Les valeurs après pos descendent d'une place : la liste ne garde aucun trou. Retirer la première valeur d'une file donne « premier arrivé, premier servi » ; retirer la dernière donne une pile (dernier arrivé, premier sorti).
local attente = {"Fer vers le dépôt A", "Charbon vers la fonderie", "Vivres pour la base"}
while #attente > 0 do
local commande = table.remove(attente, 1)
print("Départ : " .. commande)
end
print(table.remove(attente))Départ : Fer vers le dépôt A Départ : Charbon vers la fonderie Départ : Vivres pour la base nil
Sans position, table.remove d'une liste vide renvoie nil. Avec une position, c'est une erreur : table.remove(attente, 1) sur une file vide s'arrête avec bad argument #2 to 'remove' (position out of bounds). Vérifiez d'abord #attente > 0, comme ci-dessus.
Retirer en parcourant une liste. En avançant, on saute des valeurs : quand la valeur en i part, la suivante glisse en position i, et la boucle passe à i + 1. Ici, un "fini" survit :
local liste = {"fini", "fini", "à faire"}
for i, etat in ipairs(liste) do
if etat == "fini" then
table.remove(liste, i)
end
end
print(table.concat(liste, " "))fini à faire
Parcourez plutôt la liste à l'envers : retirer une valeur ne déplace que celles que vous avez déjà vues.
local taches = {
{nom = "presser des engrenages", finie = true},
{nom = "mélanger du laiton", finie = false},
{nom = "scier des planches", finie = true},
{nom = "remplir les réservoirs", finie = false},
}
for i = #taches, 1, -1 do
if taches[i].finie then
table.remove(taches, i)
end
end
for _, tache in ipairs(taches) do
print(tache.nom)
endmélanger du laiton remplir les réservoirs
Voir aussi table.insert()
Trier
Trie une liste sur place : le plus petit d'abord, ou dans l'ordre donné par une fonction.
ttable- la liste à trier, modifiée sur place
lessfunction facultatifless(a, b)renvoie true quandadoit passer avantb
La liste elle-même est modifiée, et rien n'est renvoyé. Sans fonction, les nombres vont du plus petit au plus grand, et les textes suivent l'ordre des codes de leurs caractères : les majuscules avant les minuscules, et les chiffres un par un ("10" avant "9").
local vitesses = {64, 16, 256, 32}
table.sort(vitesses)
print(table.concat(vitesses, " "))
local noms = {"zinc", "Fer", "cuivre", "Andésite"}
table.sort(noms)
print(table.concat(noms, " "))16 32 64 256 Andésite Fer cuivre zinc
Les lettres accentuées ont des codes plus grands que toutes les lettres sans accent : "étain" se range après "zinc".
Avec une fonction, c'est vous qui décidez de l'ordre. less(a, b) reçoit deux valeurs de la liste et renvoie true quand a doit passer avant b, et false sinon, y compris quand elles sont égales. Les valeurs que la fonction juge égales gardent l'ordre qu'elles avaient (le tri est stable).
local vitesses = {64, 16, 256, 32}
table.sort(vitesses, function(a, b) return a > b end)
print(table.concat(vitesses, " "))
local noms = {"zinc", "Fer", "cuivre", "Andésite"}
table.sort(noms, function(a, b) return a:lower() < b:lower() end)
print(table.concat(noms, " "))256 64 32 16 Andésite cuivre Fer zinc
Écrivez < ou > dans une fonction de tri, jamais <= ou >=. Brass ne s'arrête pas pour autant, mais les valeurs égales sortent alors dans l'ordre inverse, et un tri relancé continue de les échanger.
Les 5 objets les plus nombreux d'un coffre à objet renforcé : triez les enregistrements selon leur champ nombre, le plus grand d'abord, et affichez les cinq premiers. Le laiton et le fer ont le même nombre : ils gardent leur ordre de départ.
local coffre = {
{nom = "Pierre taillée", nombre = 2304},
{nom = "Lingot de laiton", nombre = 412},
{nom = "Pépite d'or", nombre = 37},
{nom = "Alliage d'andésite", nombre = 980},
{nom = "Lingot de cuivre", nombre = 1290},
{nom = "Lingot de zinc", nombre = 96},
{nom = "Lingot de fer", nombre = 412},
}
table.sort(coffre, function(a, b)
return a.nombre > b.nombre
end)
for i = 1, math.min(5, #coffre) do
print(string.format("%d. %-18s %5d", i, coffre[i].nom, coffre[i].nombre))
end1. Pierre taillée 2304 2. Lingot de cuivre 1290 3. Alliage d'andésite 980 4. Lingot de laiton 412 5. Lingot de fer 412
Pour départager les égalités, comparez un second champ quand le premier est égal. Avec cette fonction, le Lingot de fer passe avant le Lingot de laiton :
table.sort(coffre, function(a, b)
if a.nombre ~= b.nombre then
return a.nombre > b.nombre
end
return a.nom < b.nom
end)Erreurs. Sans fonction, les valeurs doivent être toutes des nombres ou toutes des textes : un mélange s'arrête sur attempt to compare number with string (ou string with number), et des tables sur attempt to compare two table values : triez les tables d'enregistrements avec une fonction. Un trou dans la liste s'arrête sur attempt to compare nil with number.
Coût. Sans fonction, le tri est facturé environ n × log2(n) instructions d'un coup pour n valeurs : 10 000 pour 1000 nombres, bien moins d'une seconde sur un Micro-ordinateur (1200 instructions par tick), beaucoup plus sur un Calculateur à tubes (20 par tick). Avec une fonction, chaque comparaison est un appel à celle-ci, de quelques instructions, soit environ n × log2(n) appels. Dans les deux cas, le tri s'étale sur autant de ticks qu'il faut, comme n'importe quel code, et ne bloque jamais le serveur.
Voir aussi table.keys()
Joindre et chercher
Joint les valeurs d'une liste en un seul texte, avec sep entre elles.
ttable- une liste de textes et de nombres
sepstring facultatif- le texte placé entre les valeurs (rien s'il est omis)
inumber facultatif- la première position (1 si elle est omise)
jnumber facultatif- la dernière position (
#tsi elle est omise)
- string
- les valeurs réunies en un seul texte
local trajet = {"Mine", "Fonderie", "Dépôt"}
print(table.concat(trajet, " -> "))
print(table.concat({64, 32, 16}, "+") .. " = 112")
print(table.concat(trajet, ", ", 2, 3))
print("[" .. table.concat({}) .. "]")Mine -> Fonderie -> Dépôt 64+32+16 = 112 Fonderie, Dépôt []
Les valeurs doivent être des textes ou des nombres (les nombres s'écrivent comme avec tostring). Tout le reste, true, une table, ou un trou dans la liste, s'arrête sur invalid value (at index 2) in table for 'concat' : convertissez d'abord les valeurs avec tostring. Quand i est après j, le résultat est "".
Pour construire un long texte morceau par morceau, rangez les morceaux dans une liste et joignez-les une seule fois à la fin : c'est bien moins cher que s = s .. morceau dans une boucle, qui fabrique un nouveau texte à chaque tour. Le résultat est limité à 65536 caractères (string too long).
Voir aussi string.split()
Vrai si la valeur se trouve quelque part dans la table.
ttable- la table où chercher
valueany- la valeur cherchée
- boolean
- true si l'une des valeurs de
test égale àvalue
Elle regarde les valeurs de la table, partie liste comme clés nommées, jamais les clés. Les valeurs sont comparées comme avec == : les nombres et les textes par leur contenu, les tables par identité (une autre table au contenu identique n'est pas trouvée). nil n'est jamais trouvé.
local combustibles = {"minecraft:coal", "minecraft:charcoal", "minecraft:blaze_rod"}
print(table.contains(combustibles, "minecraft:coal"))
print(table.contains(combustibles, "minecraft:stick"))
print(table.contains({vitesse = 64}, 64), table.contains({vitesse = 64}, "vitesse"))true false true false
table.contains parcourt toute la table à chaque appel. Pour tester beaucoup de valeurs contre une longue liste, construisez une fois pour toutes un ensemble, avec les valeurs comme clés, et consultez-le directement :
local combustibles = {"minecraft:coal", "minecraft:charcoal", "minecraft:blaze_rod"}
local est_combustible = {}
for _, id in ipairs(combustibles) do
est_combustible[id] = true
end
print(est_combustible["minecraft:coal"], est_combustible["minecraft:stick"])true nil
Voir aussi table.keys()
La liste des clés d'une table.
ttable- n'importe quelle table
- table
- une nouvelle liste de ses clés
L'ordre est celui de pairs : les positions 1, 2, 3... d'abord, puis les autres clés dans l'ordre où elles ont été ajoutées. Le résultat est une nouvelle liste : #table.keys(t) compte donc les entrées de n'importe quelle table, là où #t ne compte que sa partie liste. Triez les clés pour un affichage alphabétique :
local stock = {fer = 1200, cuivre = 640, zinc = 96}
local noms = table.keys(stock)
print(#stock, #noms)
table.sort(noms)
for _, nom in ipairs(noms) do
print(nom, stock[nom])
end0 3 cuivre 640 fer 1200 zinc 96
Des clés de sortes différentes (nombres et textes) ne se trient pas ensemble sans fonction.
Voir aussi pairs() table.sort()
Copier
Une copie simple d'une table : une nouvelle table avec les mêmes clés et valeurs.
ttable- la table à copier
- table
- une nouvelle table avec les mêmes clés et valeurs
Servez-vous-en pour garder une sauvegarde avant de modifier une table, ou pour confier une table à du code sans le laisser modifier la vôtre. La copie contient la partie liste et les clés nommées, dans le même ordre.
local recette = {"fer", "fer", "bâton"}
local meme = recette
local sauvegarde = table.copy(recette)
recette[3] = "or"
print(meme[3], sauvegarde[3])or bâton
« Simple » veut dire sur un seul niveau : une table rangée à l'intérieur n'est pas copiée, les deux tables la partagent. Pour une copie complète, copiez aussi les tables intérieures :
local function copie_profonde(t)
local copie = {}
for k, v in pairs(t) do
if type(v) == "table" then
copie[k] = copie_profonde(v)
else
copie[k] = v
end
end
return copie
end
local gare = {nom = "Nord", trains = {"T1", "T2"}}
local simple = table.copy(gare)
local profonde = copie_profonde(gare)
table.insert(gare.trains, "T3")
print(#simple.trains, #profonde.trains)3 2
Voir aussi table.keys()
Motifs courants
Des totaux par objet. Un inventaire liste ses emplacements un par un (inventory.list()), et le même objet peut remplir plusieurs emplacements. Additionnez-les dans une table dont les clés sont les objets :
local cases = {
{name = "minecraft:iron_ingot", count = 64},
{name = "minecraft:coal", count = 12},
{name = "minecraft:iron_ingot", count = 30},
}
local totaux = {}
for _, pile in ipairs(cases) do
totaux[pile.name] = (totaux[pile.name] or 0) + pile.count
end
for nom, nombre in pairs(totaux) do
print(nom, nombre)
endminecraft:iron_ingot 94 minecraft:coal 12
Les dernières mesures. Gardez les 5 valeurs les plus récentes d'un capteur : ajoutez à la fin, retirez la première quand il y en a trop. La recette Enregistreur de données et graphique utilise la même idée.
local historique = {}
local function retenir(valeur)
table.insert(historique, valeur)
if #historique > 5 then
table.remove(historique, 1)
end
end
for vitesse = 10, 80, 10 do
retenir(vitesse)
end
print(table.concat(historique, " "))40 50 60 70 80
Une grille. Une table de rangées, chaque rangée une table de cases : grille[y][x]. Créez chaque rangée avant de vous en servir :
local grille = {}
for y = 1, 3 do
grille[y] = {}
for x = 1, 4 do
grille[y][x] = "."
end
end
grille[2][3] = "#"
for y = 1, 3 do
print(table.concat(grille[y]))
end.... ..#. ....