Recherche…
Syntaxe
- ipairs (numeric_table) - Table Lua avec itérateur d'indices numériques
- pairs (input_table) - itérateur de table Lua générique
- key, value = next (input_table, input_key) - Sélecteur de valeur de table Lua
- table.insert (input_table, [position], value) - Insère la valeur spécifiée dans la table d'entrée
- removed_value = table.remove (input_table, [position]) - pop ou supprimer la valeur spécifiée par la position
Remarques
Les tableaux sont la seule structure de données intégrée disponible dans Lua. Ceci est soit une simplicité élégante, soit déroutante, en fonction de la façon dont vous la regardez.
Une table Lua est une collection de paires clé-valeur où les clés sont uniques et ni la clé ni la valeur ne sont nil
. En tant que tel, une table Lua peut ressembler à un dictionnaire, un hashmap ou un tableau associatif provenant d'autres langues. De nombreux motifs structurels peuvent être construits avec des tables: piles, files d'attente, ensembles, listes, graphiques, etc. Enfin, les tables peuvent être utilisées pour construire des classes dans Lua et créer un système de modules .
Lua n'applique aucune règle particulière sur la façon dont les tables sont utilisées. Les éléments contenus dans une table peuvent être un mélange de types Lua. Ainsi, par exemple, une table peut contenir des chaînes, des fonctions, des valeurs booléennes, des nombres et même d'autres tables en tant que valeurs ou clés.
Une table Lua avec des clés entières positives consécutives commençant par 1 est dite avoir une séquence. Les paires clé-valeur avec des clés entières positives sont les éléments de la séquence. D'autres langages appellent cela un tableau à 1 base. Certaines opérations et fonctions standard ne fonctionnent que sur la séquence d'une table et certaines ont un comportement non déterministe lorsqu'elles sont appliquées à une table sans séquence.
Définir une valeur dans une table à nil
supprime de la table. Les itérateurs ne verraient plus la clé associée. Lors du codage d'une table avec une séquence, il est important d'éviter de casser la séquence; Supprimez uniquement le dernier élément ou utilisez une fonction, comme la table.remove
standard.remove, qui déplace les éléments vers le bas pour combler l'écart.
Créer des tables
Créer une table vide est aussi simple que cela:
local empty_table = {}
Vous pouvez également créer une table sous la forme d'un tableau simple:
local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.
Gardez à l'esprit que par défaut, l'indexation de la table commence à 1.
Il est également possible de créer une table avec des éléments associatifs:
local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}
L'utilisation ci-dessus est du sucre syntaxique pour ce qui est ci-dessous. Les clés dans cette instance sont du type string. La syntaxe ci-dessus a été ajoutée pour faire apparaître les tables sous forme d'enregistrements. Cette syntaxe de type enregistrement est mise en parallèle avec la syntaxe d'indexation des tables avec des clés de chaîne, comme indiqué dans le didacticiel «Utilisation de base».
Comme expliqué dans la section Remarques, la syntaxe de type enregistrement ne fonctionne pas pour toutes les clés possibles. De plus, une clé peut être n'importe quelle valeur, et les exemples précédents ne couvrent que les chaînes et les numéros séquentiels. Dans d'autres cas, vous devrez utiliser la syntaxe explicite:
local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}
Tables itératives
La bibliothèque standard Lua fournit une fonction de pairs
qui parcourt les clés et les valeurs d'une table. Lors de l'itération par pairs
il n'y a pas d'ordre de traversée spécifié, même si les clés de la table sont numériques .
for key, value in pairs(input_table) do
print(key, " -- ", value)
end
Pour les tableaux utilisant des touches numériques , Lua fournit une fonction ipairs
. La fonction ipairs
toujours une itération depuis la table[1]
, la table[2]
, etc. jusqu'à ce que la première valeur nil
soit trouvée.
for index, value in ipairs(numeric_table) do
print(index, ". ", value)
end
Soyez averti que l'itération utilisant ipairs()
ne fonctionnera pas comme vous le souhaitez à quelques occasions:
input_table
contient des "trous". (Reportez-vous à la section "Éviter les lacunes dans les tableaux utilisés comme tableaux" pour plus d'informations.) Par exemple:table_with_holes = {[1] = "value_1", [3] = "value_3"}
les clés n'étaient pas toutes numériques. Par exemple:
mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
Bien sûr, ce qui suit fonctionne également pour une table qui est une séquence correcte:
for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end
Itérer une table numérique dans l'ordre inverse est facile:
for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end
Une dernière façon d'itérer sur des tables est d'utiliser le next
sélecteur dans un générique for
la boucle . Comme les pairs
il n'y a pas d'ordre spécifié pour la traversée. (La méthode des pairs
utilise next
interne. Utiliser next
est donc une version plus manuelle des pairs
. Voir les pairs
dans le manuel de référence de Lua et next
dans le manuel de référence de Lua pour plus de détails.)
for key, value in next, input_table do
print(key, value)
end
Utilisation de base
L'utilisation de table de base inclut l'accès et l'affectation d'éléments de table, l'ajout de contenu de table et la suppression du contenu de tableau. Ces exemples supposent que vous savez créer des tableaux.
Accéder aux éléments
Vu le tableau suivant,
local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
"Diarrhea", cure = "Pepto Bismol"}
On peut indexer la partie séquentielle de la table en utilisant la syntaxe d'index, l'argument de la syntaxe d'index étant la clé de la paire clé-valeur souhaitée. Comme expliqué dans le tutoriel de création, la plupart des syntaxes de déclaration sont du sucre syntaxique pour déclarer des paires clé-valeur. Les éléments inclus séquentiellement, comme les cinq premières valeurs dans example_table
, utilisent des valeurs entières croissantes comme clés; la syntaxe de l'enregistrement utilise le nom du champ sous forme de chaîne.
print(example_table[2]) --> Heartburn
print(example_table["cure"]) --> Pepto Bismol
Pour les clés de chaîne, il existe un sucre de syntaxe pour mettre en parallèle la syntaxe de style d'enregistrement pour les clés de chaîne dans la création de table. Les deux lignes suivantes sont équivalentes.
print(example_table.cure) --> Pepto Bismol
print(example_table["cure"]) --> Pepto Bismol
Vous pouvez accéder aux tables à l'aide de clés que vous n'avez jamais utilisées auparavant, ce qui n'est pas une erreur, comme c'est le cas dans d'autres langues. Cela renvoie la valeur par défaut nil
.
Affectation d'éléments
Vous pouvez modifier des éléments de table existants en les affectant à une table à l'aide de la syntaxe d'index. En outre, la syntaxe d’indexation de style d’enregistrement est également disponible pour définir des valeurs
example_table.cure = "Lots of water, the toilet, and time"
print(example_table.cure) --> Lots of water, the toilet, and time
example_table[2] = "Constipation"
print(example_table[2]) --> Constipation
Vous pouvez également ajouter de nouveaux éléments à une table existante à l'aide de l'affectation.
example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"
Remarque spéciale: certaines chaînes ne sont pas prises en charge avec la syntaxe d'enregistrement. Voir la section des remarques pour plus de détails.
Suppression d'éléments
Comme indiqué précédemment, la valeur par défaut pour une clé sans valeur assignée est nil
. Supprimer un élément d'une table est aussi simple que de redéfinir la valeur d'une clé sur sa valeur par défaut.
example_table[100] = "Face Mask"
Les éléments ne peuvent plus être distingués d'un élément non défini.
Longueur de la table
Les tableaux sont simplement des tableaux associatifs (voir remarques), mais lorsque des clés entières contiguës sont utilisées à partir de 1, la table est dite avoir une séquence .
La longueur de la partie séquence d'une table se fait en utilisant #
:
local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8
Vous pouvez utiliser l'opération length pour ajouter facilement des éléments à une table de séquence.
example_table[#example_table+1] = 'a'
print(#example_table) --> 9
Dans l'exemple ci-dessus, la valeur précédente de #example_table
est 8
, l'ajout de 1
vous donne la prochaine clé entière valide dans la séquence, 9
, donc ... example_table[9] = 'a'
. Cela fonctionne pour n'importe quelle longueur de table.
Remarque spéciale: L' utilisation de clés entières qui ne sont pas contiguës et qui partent de 1 rompt la séquence, ce qui fait de la table une table fragmentée . Le résultat de l'opération de longueur est indéfini dans ce cas. Voir la section des remarques.
Utilisation des fonctions de la bibliothèque de tables pour ajouter / supprimer des éléments
Une autre façon d'ajouter des éléments à une table est la fonction table.insert()
. La fonction d'insertion ne fonctionne que sur les tables de séquence. Il y a deux façons d'appeler la fonction. Le premier exemple montre le premier usage, où on spécifie l'index pour insérer l'élément (le second argument). Cela pousse tous les éléments de l'index donné à #table
jusqu'à une position. Le deuxième exemple montre l’autre utilisation de table.insert()
, où l’index n’est pas spécifié et la valeur donnée est ajoutée à la fin de la table (index #table + 1
).
local t = {"a", "b", "d", "e"}
table.insert(t, 3, "c") --> t = {"a", "b", "c", "d", "e"}
t = {"a", "b", "c", "d"}
table.insert(t, "e") --> t = {"a", "b", "c", "d", "e"}
La table.insert()
parallèle à table.insert()
pour supprimer des éléments est table.remove()
. De même, il a deux sémantiques d'appel: une pour supprimer des éléments à une position donnée et une autre pour supprimer de la fin de la séquence. Lors de la suppression du milieu d'une séquence, tous les éléments suivants sont décalés d'un index vers le bas.
local t = {"a", "b", "c", "d", "e"}
local r = table.remove(t, 3) --> t = {"a", "b", "d", "e"}, r = "c"
t = {"a", "b", "c", "d", "e"}
r = table.remove(t) --> t = {"a", "b", "c", "d"}, r = "e"
Ces deux fonctions mutent la table donnée. Comme vous pourriez être en mesure de dire la deuxième méthode d'appeler table.insert()
et table.remove()
fournit la sémantique de la pile aux tables. En tirant parti de cela, vous pouvez écrire du code comme l'exemple ci-dessous.
function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end
Il implémente le Fisher-Yates Shuffle, peut-être de manière inefficace. Il utilise la table.insert()
pour ajouter l'élément extrait de manière aléatoire à la fin de la même table, et la table.remove()
pour extraire aléatoirement un élément de la partie restante sans mélange de la table.
Éviter les lacunes dans les tableaux utilisés comme tableaux
Définir nos termes
Par tableau , nous entendons ici une table Lua utilisée comme séquence. Par exemple:
-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}
Nous utilisons cette table comme une séquence: un groupe d'éléments indexés par des entiers. De nombreuses langues appellent cela un tableau, et nous aussi. Mais à proprement parler, il n’ya pas de tableau dans Lua. Il n'y a que des tables, dont certaines ressemblent à des tableaux, d'autres à la forme de hachage (ou de dictionnaire, si vous préférez), et d'autres sont mélangées.
Un point important à propos de notre tableau pour pets
est qu'il n'y a pas de lacunes. Le premier élément, pets[1]
, est la chaîne "dogs", le deuxième élément, pets[2]
, est la chaîne "cats", et le dernier élément, pets[3]
, est "birds". La bibliothèque standard de Lua et la plupart des modules écrits pour Lua supposent 1 comme premier index pour les séquences. Un tableau sans gabarit a donc des éléments de 1..n
sans manquer aucun nombre dans la séquence. (Dans le cas limite, n = 1
, et le tableau ne contient qu’un élément.)
Lua fournit la fonction ipairs
pour parcourir ces tables.
-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Ceci imprimerait "les objets à la position 1 sont les chiens", "les objets à la position 2 sont les chats", "les objets à la position 3 sont les oiseaux".
Mais que se passe-t-il si nous faisons ce qui suit?
local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Un tableau tel que ce deuxième exemple est un tableau fragmenté. Il y a des lacunes dans la séquence. Ce tableau ressemble à ceci:
{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12
Les valeurs nulles ne prennent aucune mémoire supplémentaire; lua en interne ne sauvegarde que les valeurs [1] = "dogs"
, [2] = "cats"
, [3] = "birtds"
et [12] = "goldfish"
Pour répondre à la question immédiate, ipairs
s'arrêtera après les oiseaux; "goldfish" sur les pets[12]
ne sera jamais atteint à moins que nous ajustions notre code. Ceci est dû au fait que ipairs
itération à partir de 1..n-1
où n
est la position du premier nil
trouvé. Lua définit la table[length-of-table + 1]
comme étant nil
. Donc, dans une séquence correcte, l'itération s'arrête lorsque Lua essaie, par exemple, d'obtenir le quatrième élément d'un tableau à trois éléments.
Quand?
Les deux endroits les plus courants pour les problèmes qui surviennent avec les baies éparses sont (i) lorsque vous essayez de déterminer la longueur du tableau et (ii) lorsque vous essayez de parcourir le tableau. En particulier:
- Lorsque vous utilisez l'opérateur de longueur
#
, l'opérateur de longueur cesse de compter au premiernil
trouvé. - Lorsque vous utilisez la fonction
ipairs()
, comme mentionné ci-dessus, elle cesse d’itérer au premiernil
trouvé. - Lorsque vous utilisez la fonction
table.unpack()
car cette méthode arrête la décompression au premiernil
trouvé. - Lorsque vous utilisez d'autres fonctions qui accèdent (directement ou indirectement) à l'une des fonctions ci-dessus.
Pour éviter ce problème, il est important d'écrire votre code afin que si vous vous attendez à ce qu'une table soit un tableau, vous n'introduisez pas de lacunes. Les lacunes peuvent être introduites de plusieurs manières:
- Si vous ajoutez quelque chose à un tableau au mauvais endroit.
- Si vous insérez une valeur
nil
dans un tableau. - Si vous supprimez des valeurs d'un tableau.
Vous pourriez penser: "Mais je ne ferais jamais aucune de ces choses." Eh bien, pas intentionnellement, mais voici un exemple concret de la façon dont les choses pourraient mal tourner. Imaginez que vous vouliez écrire une méthode de filtrage pour Lua comme select
de Ruby et grep
de Perl. La méthode acceptera une fonction de test et un tableau. Il itère sur le tableau, appelant la méthode de test sur chaque élément à son tour. Si l'élément passe, cet élément est ajouté à un tableau de résultats renvoyé à la fin de la méthode. Ce qui suit est une mise en œuvre boguée:
local filter = function (fun, t)
local res = {}
for idx, item in ipairs(t) do
if fun(item) then
res[idx] = item
end
end
return res
end
Le problème est que lorsque la fonction retourne false
, nous sautons un nombre dans la séquence. Imaginez un filter(isodd, {1,2,3,4,5,6,7,8,9,10})
appel filter(isodd, {1,2,3,4,5,6,7,8,9,10})
: il y aura des lacunes dans la table renvoyée chaque fois qu'un nombre pair sera passé dans le tableau à filter
.
Voici une implémentation fixe:
local filter = function (fun, t)
local res = {}
for _, item in ipairs(t) do
if fun(item) then
res[#res + 1] = item
end
end
return res
end
Conseils
- Utilisez les fonctions standard:
table.insert(<table>, <value>)
ajoute toujours à la fin du tableau.table[#table + 1] = value
est un raccourci pour cela.table.remove(<table>, <index>)
déplacera toutes les valeurs suivantes pour combler le vide (ce qui peut également le ralentir). - Vérifiez les valeurs
nil
avant d' insérer, en évitant des choses commetable.pack(function_call())
, qui peuvent introduire des valeursnil
dans notre table. - Vérifier les valeurs
nil
après l' insertion et, si nécessaire, combler le vide en déplaçant toutes les valeurs consécutives. - Si possible, utilisez des valeurs d'espace réservé. Par exemple, changez la valeur
nil
pour0
ou une autre valeur d'espace réservé. - Si des lacunes sont inévitables, cela devrait être correctement documenté (commenté).
- Ecrivez une
__len()
et utilisez l'opérateur#
.
Exemple pour 6 .:
tab = {"john", "sansa", "daenerys", [10] = "the imp"}
print(#tab) --> prints 3
setmetatable(tab, {__len = function() return 10 end})
-- __len needs to be a function, otherwise it could just be 10
print(#tab) --> prints 10
for i=1, #tab do print(i, tab[i]) end
--> prints:
-- 1 john
-- 2 sansa
-- 3 daenerys
-- 4 nil
-- ...
-- 10 the imp
for key, value in ipairs(tab) do print(key, value) end
--> this only prints '1 john \n 2 sansa \n 3 daenerys'
Une autre alternative consiste à utiliser la fonction pairs()
et à filtrer les indices non entiers:
for key in pairs(tab) do
if type(key) == "number" then
print(key, tab[key]
end
end
-- note: this does not remove float indices
-- does not iterate in order