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-1n 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 premier nil trouvé.
  • Lorsque vous utilisez la fonction ipairs() , comme mentionné ci-dessus, elle cesse d’itérer au premier nil trouvé.
  • Lorsque vous utilisez la fonction table.unpack() car cette méthode arrête la décompression au premier nil 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

  1. 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).
  2. Vérifiez les valeurs nil avant d' insérer, en évitant des choses comme table.pack(function_call()) , qui peuvent introduire des valeurs nil dans notre table.
  3. 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.
  4. Si possible, utilisez des valeurs d'espace réservé. Par exemple, changez la valeur nil pour 0 ou une autre valeur d'espace réservé.
  5. Si des lacunes sont inévitables, cela devrait être correctement documenté (commenté).
  6. 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


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow