Ricerca…
Sintassi
- ipairs (numeric_table) - Tabella di Lua con iteratore di indici numerici
- pair (input_table) - generico iteratore di tabella Lua
- key, value = next (input_table, input_key) - Selettore del valore della tabella Lua
- table.insert (input_table, [position], value) - inserisce il valore specificato nella tabella di input
- removed_value = table.remove (input_table, [position]): popout o rimozione del valore specificato dalla posizione
Osservazioni
Le tabelle sono l'unica struttura dati incorporata disponibile in Lua. Questa è elegante semplicità o confusa, a seconda di come la si guarda.
Una tabella Lua è una raccolta di coppie chiave-valore in cui le chiavi sono univoche e né la chiave né il valore sono nil
. In quanto tale, una tabella Lua può assomigliare ad un dizionario, hashmap o array associativo proveniente da altre lingue. Molti schemi strutturali possono essere creati con tabelle: stack, code, set, elenchi, grafici, ecc. Infine, le tabelle possono essere utilizzate per creare classi in Lua e per creare un sistema di moduli .
Lua non applica regole particolari su come vengono utilizzate le tabelle. Gli oggetti contenuti in una tabella possono essere una miscela di tipi Lua. Ad esempio, una tabella può contenere stringhe, funzioni, valori booleani, numeri e persino altre tabelle come valori o chiavi.
Una tabella Lua con tasti interi positivi consecutivi che iniziano con 1 è detta avere una sequenza. Le coppie chiave-valore con chiavi intere positive sono gli elementi della sequenza. Altre lingue chiamano questo array basato su 1. Alcune operazioni e funzioni standard funzionano solo sulla sequenza di una tabella e alcune hanno un comportamento non deterministico quando vengono applicate a una tabella senza una sequenza.
L'impostazione di un valore in una tabella su nil
rimuove dalla tabella. Gli iteratori non vedrebbero più la chiave correlata. Quando si codifica per una tabella con una sequenza, è importante evitare di interrompere la sequenza; Rimuovere solo l'ultimo elemento o utilizzare una funzione, come la table.remove
standard.remove, che sposta gli elementi verso il basso per colmare il divario.
Creare tabelle
La creazione di una tabella vuota è semplice come questa:
local empty_table = {}
Puoi anche creare una tabella sotto forma di un semplice array:
local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.
Tieni presente che, per impostazione predefinita, l'indicizzazione della tabella inizia da 1.
È anche possibile creare una tabella con elementi associativi:
local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}
L'uso sopra è lo zucchero di sintassi per ciò che è sotto. Le chiavi in questa istanza sono del tipo, stringa. La sintassi precedente è stata aggiunta per far apparire le tabelle come record. Questa sintassi in stile record è parallela alla sintassi per l'indicizzazione delle tabelle con le chiavi stringa, come mostrato nell'esercitazione sull'uso di base.
Come spiegato nella sezione commenti, la sintassi stile record non funziona per ogni chiave possibile. Inoltre una chiave può essere qualsiasi valore di qualsiasi tipo, e gli esempi precedenti riguardavano solo stringhe e numeri sequenziali. In altri casi dovrai utilizzare la sintassi esplicita:
local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}
Tabella di iterazione
La libreria standard Lua fornisce una funzione di pairs
che itera su chiavi e valori di una tabella. Durante l'iterazione con pairs
non esiste un ordine specificato per attraversare, anche se i tasti della tabella sono numerici .
for key, value in pairs(input_table) do
print(key, " -- ", value)
end
Per le tabelle che utilizzano i tasti numerici , Lua fornisce una funzione di ipairs
. La funzione ipairs
eseguirà sempre iterazioni dalla table[1]
, dalla table[2]
, ecc. Fino a trovare il primo valore nil
.
for index, value in ipairs(numeric_table) do
print(index, ". ", value)
end
Tieni presente che l'iterazione usando ipairs()
non funzionerà come potresti volere in poche occasioni:
input_table
ha "buchi" in esso. (Per ulteriori informazioni, consultare la sezione "Come evitare gli spazi vuoti nelle tabelle utilizzate come matrici".) Ad esempio:table_with_holes = {[1] = "value_1", [3] = "value_3"}
le chiavi non erano tutte numeriche. Per esempio:
mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
Naturalmente, quanto segue funziona anche per una tabella che è una sequenza corretta:
for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end
L'iterazione di una tabella numerica in ordine inverso è semplice:
for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end
Un ultimo modo per scorrere le tabelle è usare il next
selettore in un ciclo for
generico . Come pairs
non esiste un ordine specifico per attraversare. (Il metodo delle pairs
usa il next
internamente, quindi usare il next
è essenzialmente una versione più manuale delle pairs
pairs
nel manuale di riferimento di Lua e next
nel manuale di riferimento di Lua per maggiori dettagli.)
for key, value in next, input_table do
print(key, value)
end
Uso di base
L'utilizzo della tabella di base include l'accesso e l'assegnazione di elementi di tabella, l'aggiunta di contenuto di tabella e la rimozione del contenuto della tabella. Questi esempi presumono che tu sappia come creare tabelle.
Accesso agli elementi
Data la seguente tabella,
local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
"Diarrhea", cure = "Pepto Bismol"}
Si può indicizzare la parte sequenziale della tabella usando la sintassi dell'indice, l'argomento della sintassi dell'indice è la chiave della coppia chiave-valore desiderata. Come spiegato nel tutorial di creazione, la maggior parte della sintassi delle dichiarazioni è zucchero sintattico per la dichiarazione delle coppie chiave-valore. Gli elementi inclusi sequenzialmente, come i primi cinque valori in example_table
, usano valori interi crescenti come chiavi; la sintassi del record usa il nome del campo come una stringa.
print(example_table[2]) --> Heartburn
print(example_table["cure"]) --> Pepto Bismol
Per le chiavi di stringa c'è lo zucchero di sintassi per mettere in parallelo la sintassi stile record per le chiavi stringa nella creazione della tabella. Le due righe seguenti sono equivalenti.
print(example_table.cure) --> Pepto Bismol
print(example_table["cure"]) --> Pepto Bismol
Puoi accedere alle tabelle usando le chiavi che non hai mai usato prima, non è un errore come in altre lingue. In questo modo restituisce il valore predefinito nil
.
Assegnazione di elementi
È possibile modificare elementi di tabella esistenti assegnandoli a una tabella utilizzando la sintassi dell'indice. Inoltre, la sintassi di indicizzazione in stile record è disponibile anche per l'impostazione dei valori
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
Puoi anche aggiungere nuovi elementi a una tabella esistente usando il compito.
example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"
Nota speciale: alcune stringhe non sono supportate con la sintassi del record. Vedere la sezione commenti per i dettagli.
Rimozione di elementi
Come affermato in precedenza, il valore predefinito per una chiave senza valore assegnato è nil
. Rimuovere un elemento da una tabella è semplice come reimpostare il valore di una chiave sul valore predefinito.
example_table[100] = "Face Mask"
Gli elementi sono ora indistinguibili da un elemento non impostato.
Lunghezza della tabella
Le tabelle sono semplicemente array associativi (vedi osservazioni), ma quando si usano chiavi intere contigue a partire da 1 la tabella si dice abbia una sequenza .
La ricerca della lunghezza della parte di sequenza di una tabella viene eseguita utilizzando #
:
local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8
È possibile utilizzare l'operazione di lunghezza per aggiungere facilmente elementi a una tabella di sequenze.
example_table[#example_table+1] = 'a'
print(#example_table) --> 9
Nell'esempio precedente, il valore precedente di #example_table
è 8
, l'aggiunta 1
ti dà la successiva chiave intera valida nella sequenza, 9
, quindi ... example_table[9] = 'a'
. Questo funziona per qualsiasi lunghezza del tavolo.
Nota speciale: l' utilizzo di chiavi intere non contigue e a partire da 1 interrompe la sequenza trasformando la tabella in una tabella sparsa . In questo caso il risultato dell'operazione di lunghezza non è definito. Vedi la sezione commenti.
Utilizzo delle funzioni della libreria di tabella per aggiungere / rimuovere elementi
Un altro modo per aggiungere elementi a una tabella è la funzione table.insert()
. La funzione di inserimento funziona solo sulle tabelle di sequenza. Esistono due modi per chiamare la funzione. Il primo esempio mostra il primo utilizzo, in cui si specifica l'indice per inserire l'elemento (il secondo argomento). Ciò spinge tutti gli elementi dall'indice dato a #table
su una posizione. Il secondo esempio mostra l'altro utilizzo di table.insert()
, in cui l'indice non è specificato e il valore dato viene aggiunto alla fine della tabella (indice #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"}
Per table.insert()
parallelo per la rimozione di elementi è table.remove()
. Allo stesso modo ha due semantiche di chiamata: una per rimuovere elementi in una determinata posizione e un'altra per rimuovere dalla fine della sequenza. Quando si rimuove dal centro di una sequenza, tutti gli elementi seguenti vengono spostati di un indice.
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"
Queste due funzioni mutano la tabella data. Come si potrebbe essere in grado di dire il secondo metodo di chiamata table.insert()
e table.remove()
fornisce semantica dello stack alle tabelle. Facendo leva su questo, puoi scrivere il codice come nell'esempio qui sotto.
function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end
Implementa la Fisher-Yates Shuffle, forse in modo inefficiente. Usa table.insert()
per aggiungere l'elemento estratto a caso alla fine della stessa tabella e table.remove()
per estrarre in modo casuale un elemento dalla parte rimanente della tabella non mescolata.
Evitare gli spazi vuoti nelle tabelle utilizzate come array
Definire i nostri termini
Per array qui intendiamo una tabella Lua usata come sequenza. Per esempio:
-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}
Stiamo usando questa tabella come una sequenza: un gruppo di elementi immessi da numeri interi. Molte lingue chiamano questo array, e anche noi. Ma a rigor di termini, non c'è niente come un array a Lua. Ci sono solo tabelle, alcune delle quali sono simili ad array, alcune delle quali sono simili a hash (o simili a un dizionario, se preferite), e alcune sono miste.
Un punto importante sul nostro array di pets
è che non ha lacune. Il primo oggetto, pets[1]
, è la stringa "cani", il secondo oggetto, pets[2]
, è la stringa "gatti", e l'ultimo oggetto, pets[3]
, è "uccelli". La libreria standard di Lua e la maggior parte dei moduli scritti per Lua assumono 1 come primo indice per le sequenze. Un array gapless ha quindi elementi da 1..n
senza perdere alcun numero nella sequenza. (Nel caso limite, n = 1
, e l'array ha solo un elemento in esso.)
Lua fornisce gli ipairs
funzione ipairs
per iterare su tali tabelle.
-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Questo stamperebbe "L'oggetto in posizione 1 è un cane.", "L'oggetto in posizione 2 è un gatto.", "L'oggetto in posizione 3 è un uccello."
Ma cosa succede se facciamo quanto segue?
local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Una matrice come questo secondo esempio è una matrice sparsa. Ci sono lacune nella sequenza. Questo array è simile al seguente:
{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12
I valori nulli non occupano alcuna memoria aggiuntiva; internamente lua salva solo i valori [1] = "dogs"
, [2] = "cats"
, [3] = "birtds"
e [12] = "goldfish"
Per rispondere alla domanda immediata, gli ipairs
si fermeranno dopo gli uccelli; "goldfish" negli pets[12]
non sarà mai raggiunto a meno che non modifichiamo il nostro codice. Questo perché gli ipairs
1..n-1
da 1..n-1
dove n
è la posizione del primo nil
trovato. Lua definisce la table[length-of-table + 1]
come nil
. Quindi, in una sequenza corretta, l'iterazione si interrompe quando Lua cerca di ottenere, per esempio, il quarto elemento in un array di tre elementi.
Quando?
Le due posizioni più comuni per i problemi che sorgono con gli array sparsi sono (i) quando si tenta di determinare la lunghezza dell'array e (ii) quando si tenta di ripetere l'array. In particolare:
- Quando si utilizza l'operatore di lunghezza
#
poiché l'operatore di lunghezza smette di contare al primonil
trovato. - Quando si utilizza la funzione
ipairs()
poiché come detto sopra, si interrompe l'iterazione al primonil
trovato. - Quando si utilizza la funzione
table.unpack()
poiché questo metodo interrompe la decompressione al primonil
trovato. - Quando si utilizzano altre funzioni che (direttamente o indirettamente) accedono a uno dei precedenti.
Per evitare questo problema, è importante scrivere il codice in modo che se si prevede che una tabella sia un array, non si introducono spazi vuoti. Gli spazi possono essere introdotti in diversi modi:
- Se si aggiunge qualcosa a un array nella posizione sbagliata.
- Se si inserisce un valore
nil
in una matrice. - Se rimuovi i valori da una matrice.
Potresti pensare: "Ma non farei mai nessuna di quelle cose". Bene, non intenzionalmente, ma ecco un esempio concreto di come le cose potrebbero andare storte. Immagina di voler scrivere un metodo di filtro per Lua come Ruby's select
e Perl's grep
. Il metodo accetterà una funzione di test e un array. Itera su tutta la serie, chiamando a turno il metodo di prova su ciascun oggetto. Se l'oggetto passa, quell'elemento viene aggiunto ad una matrice di risultati che viene restituita alla fine del metodo. Quanto segue è un'implementazione bacata:
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
Il problema è che quando la funzione restituisce false
, saltiamo un numero nella sequenza. Immagina di chiamare il filter(isodd, {1,2,3,4,5,6,7,8,9,10})
: ci saranno degli spazi nella tabella restituita ogni volta che c'è un numero pari nell'array passato al filter
.
Ecco un'implementazione fissa:
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
Suggerimenti
- Usa le funzioni standard:
table.insert(<table>, <value>)
si aggiunge sempre alla fine dell'array.table[#table + 1] = value
è una mano breve per questo.table.remove(<table>, <index>)
sposterà tutti i seguenti valori per riempire il gap (che può anche rallentare). - Controlla i valori
nil
prima di inserirli, evitando cose cometable.pack(function_call())
, che potrebbe introdurre valorinil
nella nostra tabella. - Verificare i valori
nil
dopo l' inserimento e, se necessario, riempiendo lo spazio spostando tutti i valori consecutivi. - Se possibile, utilizza valori segnaposto. Ad esempio, cambia
nil
per0
o un altro valore di segnaposto. - Se lasciare spazi vuoti è inevitabile, questo dovrebbe essere documentato in modo appropriato (commentato).
- Scrivi un
__len()
e usa l'operatore#
.
Esempio per 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'
Un'altra alternativa è usare la funzione pairs()
e filtrare gli indici non interi:
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