Sök…
Syntax
- ipairs (numeric_table) - Lua-tabell med iterator för numeriska index
- par (input_table) - generisk Lua-tabell iterator
- nyckel, värde = nästa (input_table, input_key) - Lua tabellvärdesväljare
- table.insert (input_table, [position], value) - infoga angivet värde i ingångstabellen
- remove_value = table.remove (input_table, [position]) - pop sist eller ta bort värde som anges efter position
Anmärkningar
Tabeller är den enda inbyggda datastrukturen som finns i Lua. Detta är antingen elegant enkelhet eller förvirrande, beroende på hur du ser på det.
Ett Lua-bord är en samling nyckelvärdespar där nycklarna är unika och varken nyckeln eller värdet är nil
. Som sådan kan en Lua-tabell likna en ordlista, hashmap eller associerande matris från andra språk. Många strukturella mönster kan byggas med tabeller: staplar, köer, uppsättningar, listor, grafer osv. Slutligen kan tabeller användas för att bygga klasser i Lua och för att skapa ett modulsystem .
Lua verkställer inga särskilda regler för hur tabeller används. Objekten i en tabell kan vara en blandning av Lua-typer. Så till exempel kan en tabell innehålla strängar, funktioner, booleaner, siffror och till och med andra tabeller som värden eller nycklar.
Ett Lua-bord med på varandra följande positiva heltalstangenter som börjar med 1 sägs ha en sekvens. Nyckelvärdeparen med positiva heltalstangenter är elementen i sekvensen. Andra språk kallar detta en 1-baserad matris. Vissa standardoperationer och funktioner fungerar endast på en tabells sekvens och vissa har icke-deterministiskt beteende när de tillämpas på en tabell utan en sekvens.
Om du ställer in ett värde i en tabell till nil
tas det bort från tabellen. Iteratorer skulle inte längre se den relaterade nyckeln. När du kodar för ett bord med en sekvens är det viktigt att undvika att sekvensen bryts; Ta bara bort det sista elementet eller använd en funktion, som standardtabellen. table.remove
, som förskjuter element för att stänga avståndet.
Skapa tabeller
Att skapa en tom tabell är så enkelt som detta:
local empty_table = {}
Du kan också skapa en tabell i form av en enkel matris:
local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.
Tänk på att tabellindex som standard börjar på 1.
Det är också möjligt att skapa en tabell med associerande element:
local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}
Användningen ovan är syntaxsocker för vad som är nedan. Nycklarna i det här fallet är av typen sträng. Ovanstående syntax lades till för att få tabeller att visas som poster. Den här posten-syntaxen är parallell med syntaxen för indexering av tabeller med strängnycklar, som det ses i "grundläggande användning" -handledning.
Som förklarats i anmärkningsavsnittet fungerar inte inspelningssyntaxen för alla möjliga nycklar. Dessutom kan en nyckel vara valfritt värde av vilken typ som helst, och de tidigare exemplen omfattade endast strängar och sekvensnummer. I andra fall måste du använda den uttryckliga syntaxen:
local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}
Iterating bord
Lua-standardbiblioteket tillhandahåller en pairs
som upprepas över tabellernas tangenter och värden. Vid iterering med pairs
finns ingen specificerad ordning för genomgång, även om tabellerna i tabellen är numeriska .
for key, value in pairs(input_table) do
print(key, " -- ", value)
end
För tabeller med sifferknappar tillhandahåller Lua en ipairs
funktion. ipairs
funktionen ipairs
alltid från table[1]
, table[2]
osv. Tills det första nil
hittas.
for index, value in ipairs(numeric_table) do
print(index, ". ", value)
end
Varnas för att iteration med ipairs()
inte fungerar som du kanske vill vid några tillfällen:
input_table
har "hål" i det. (Se avsnittet "Undvika luckor i tabeller som används som matriser" för mer information.) Till exempel:table_with_holes = {[1] = "value_1", [3] = "value_3"}
knapparna var inte alla numeriska. Till exempel:
mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
Naturligtvis fungerar följande också för en tabell som är en korrekt sekvens:
for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end
Det är enkelt att skriva en numerisk tabell i omvänd ordning:
for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end
Ett sista sätt att iterera över tabeller är att använda next
väljare i en generisk for
loop . Liksom pairs
finns det ingen specifik beställning för genomgång. ( pairs
använder next
internt. Så att använda next
är i huvudsak en mer manuell version av pairs
. Se pairs
i Luas referensmanual och next
i Luas referenshandbok för mer information.)
for key, value in next, input_table do
print(key, value)
end
Grundläggande användning
Grundläggande tabellanvändning inkluderar åtkomst och tilldelning av tabellelement, tillägg av tabellinnehåll och borttagning av tabellinnehåll. Dessa exempel antar att du vet hur du skapar tabeller.
Åtkomst till element
Följande tabell,
local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
"Diarrhea", cure = "Pepto Bismol"}
Man kan indexera den sekvensiella delen av tabellen genom att använda indexsyntaxen, varvid argumentet till indexsyntaxen är nyckeln till det önskade nyckelvärdesparet. Som förklarats i skapelsetutorialen är de flesta av deklarationssyntaxen syntaktiskt socker för att deklarera nyckelvärdespar. Sekventiellt inkluderade element, som de första fem värdena i example_table
, använder ökande heltal som nycklar; postens syntax använder fältets namn som en sträng.
print(example_table[2]) --> Heartburn
print(example_table["cure"]) --> Pepto Bismol
För strängnycklar finns det syntaxsocker för att parallella syntaxen för post-stil för strängnycklar i tabellskapandet Följande två rader är likvärdiga.
print(example_table.cure) --> Pepto Bismol
print(example_table["cure"]) --> Pepto Bismol
Du kan komma åt tabeller med nycklar som du inte har använt tidigare, det är inte ett fel som på andra språk. Om du gör det returneras standardvärdet nil
.
Tilldela element
Du kan ändra befintliga tabellelement genom att tilldela en tabell med indexsyntaxen. Dessutom är indexsyntaxen för inspelningsstil också tillgänglig för inställning av värden
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
Du kan också lägga till nya element i en befintlig tabell med uppdrag.
example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"
Speciell anmärkning: Vissa strängar stöds inte med post-syntaxen. Se kommentaravsnittet för detaljer.
Ta bort element
Som nämnts tidigare är standardvärdet för en nyckel utan tilldelat värde nil
. Att ta bort ett element från en tabell är lika enkelt som att återställa värdet på en tangent till standardvärdet.
example_table[100] = "Face Mask"
Elementen kan nu inte skiljas från ett oinställt element.
Bordslängd
Tabeller är helt enkelt associerande matriser (se anmärkningar), men när sammanhängande heltalstangenter används från 1 sägs att tabellen har en sekvens .
Att hitta längden på sekvensdelen i en tabell görs med #
:
local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8
Du kan använda längdfunktionen för att enkelt lägga till objekt i en sekvenstabell.
example_table[#example_table+1] = 'a'
print(#example_table) --> 9
I exemplet ovan är det föregående värdet för #example_table
8
, genom att lägga till 1
får du nästa giltiga heltalsknapp i sekvensen, 9
, så ... example_table[9] = 'a'
. Detta fungerar för alla bordslängder.
Speciell anmärkning: Använda heltalsknappar som inte är sammanhängande och börjar från 1 bryter sekvensen som gör tabellen till ett glest bord . Resultatet av längdoperationen är inte definierat i det fallet. Se kommentarerna.
Använda tabellbibliotekets funktioner för att lägga till / ta bort element
Ett annat sätt att lägga till element i en tabell är table.insert()
. Insättningsfunktionen fungerar bara på sekvensbord. Det finns två sätt att ringa funktionen. Det första exemplet visar den första användningen, där man anger indexet för att infoga elementet (det andra argumentet). Detta skjuter alla element från det givna indexet till #table
upp en position. Det andra exemplet visar den andra användningen av table.insert()
, där indexet inte specificeras och det givna värdet läggs till i slutet av tabellen (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"}
Att parallella table.insert()
för att ta bort element är table.remove()
. På liknande sätt har den två kallande semantik: en för att ta bort element på en given position, och en annan för att ta bort från slutet av sekvensen. När du tar bort från mitten av en sekvens flyttas alla följande element ned ett index.
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"
Dessa två funktioner muterar den givna tabellen. Som du kanske kan säga den andra metoden att ringa table.insert()
och table.remove()
ger stack semantics till tabeller. Utnyttja det kan du skriva kod som i exemplet nedan.
function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end
Det implementerar Fisher-Yates Shuffle, kanske ineffektivt. Den använder table.insert()
att lägga till det slumpmässigt extraherade elementet i slutet av samma tabell, och table.remove()
att slumpmässigt extrahera ett element från den återstående icke-blandade delen av tabellen.
Undvika luckor i tabeller som används som matriser
Definiera våra villkor
Med array här menar vi en Lua-tabell som används som en sekvens. Till exempel:
-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}
Vi använder den här tabellen som en sekvens: en grupp av artiklar som är nycklade av heltal. Många språk kallar detta för en grupp, och det kommer vi också att göra. Men strikt talat finns det inget sådant som en matris i Lua. Det finns bara tabeller, av vilka några är array-liknande, några är hash-liknande (eller ordbok-liknande, om du föredrar), och några av dem är blandade.
En viktig punkt om våra pets
array är att det inte har några luckor. Den första artikeln, pets[1]
, är strängen "hundar", den andra artikeln, pets[2]
, är strängen "katter", och den sista artikeln, pets[3]
, är "fåglar". Luas standardbibliotek och de flesta moduler skrivna för Lua antar 1 som det första indexet för sekvenser. En gapless array har därför objekt från 1..n
utan att sakna några nummer i sekvensen. (I det begränsande fallet, n = 1
, och matrisen har bara ett objekt i det.)
Lua tillhandahåller den inbyggda funktionen ipairs
att iterera över sådana tabeller.
-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Detta skulle skriva ut "Objekt på plats 1 är hundar.", "Objekt på plats 2 är katter.", "Objekt på plats 3 är fåglar."
Men vad händer om vi gör följande?
local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
En matris som det här andra exemplet är en gles grupp. Det finns luckor i sekvensen. Det här arrayet ser ut så här:
{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12
Nollvärdena tar inte upp något extra minne; internt lua sparar bara värdena [1] = "dogs"
, [2] = "cats"
, [3] = "birtds"
och [12] = "goldfish"
För att besvara den omedelbara frågan ipairs
efter fåglar; "guldfisk" på pets[12]
kommer aldrig att nås om vi inte justerar vår kod. Detta beror på att ipairs
upprepas från 1..n-1
där n
är positionen för den första nil
hittades. Lua definierar table[length-of-table + 1]
att vara nil
. Så i rätt ordning slutar iterationen när Lua försöker få, säg, den fjärde artikeln i en grupp med tre artiklar.
När?
De två vanligaste platserna för problem som kan uppstå med glesa matriser är (i) när man försöker bestämma längden på matrisen och (ii) när man försöker iterera över matrisen. Särskilt:
- När du använder operatören
#
längd eftersom längdoperatören slutar räkna vid den förstanil
hittades. - När du använder
ipairs()
eftersom den som nämnts ovan slutar att iterera vid den förstanil
hittades. - När du använder
table.unpack()
eftersom denna metod slutar packa upp vid den förstanil
hittades. - När du använder andra funktioner som (direkt eller indirekt) får åtkomst till någon av ovanstående.
För att undvika det här problemet är det viktigt att skriva din kod så att om du förväntar dig att en tabell är en matris, inte introducerar du luckor. Gap kan införas på flera sätt:
- Om du lägger till något i en grupp i fel position.
- Om du sätter in ett
nil
i en matris. - Om du tar bort värden från en matris.
Du kanske tänker, "Men jag skulle aldrig göra någon av dessa saker." Tja, inte avsiktligt, men här är ett konkret exempel på hur saker och ting kan gå fel. Föreställ dig att du vill skriva en filtermetod för Lua som Rubys select
och Perls grep
. Metoden accepterar en testfunktion och en matris. Det iterates över matrisen och kallar testmetoden för varje artikel i tur och ordning. Om objektet passerar, läggs det objektet till en resultatuppsättning som returneras i slutet av metoden. Följande är en buggy-implementering:
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
Problemet är att när funktionen returnerar false
hoppar vi över ett nummer i sekvensen. Föreställ dig att ringa filter(isodd, {1,2,3,4,5,6,7,8,9,10})
: det kommer att finnas luckor i den returnerade tabellen varje gång det finns ett jämnt tal i matrisen som skickas för att filter
.
Här är en fast implementering:
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
tips
- Använd standardfunktioner:
table.insert(<table>, <value>)
läggs alltid till i slutet av matrisen.table[#table + 1] = value
är en kort hand för detta.table.remove(<table>, <index>)
kommer att flytta alla följande värden tillbaka för att fylla mellanrummet (vilket också kan göra det långsamt). - Se efter
nil
innan du sätter i och undvik saker somtable.pack(function_call())
, som kan smyganil
i vårt bord. - Kontrollera efter
nil
efter infogning och fyll vid behov på mellanrummet genom att växla alla på varandra följande värden. - Använd om möjligt värden för platshållare. Ändra till exempel
nil
för0
eller något annat platshållarvärde. - Om det är oundvikligt att lämna luckor, bör detta dokumenteras (kommenteras).
- Skriv en
__len()
metametod och använd operatorn#
.
Exempel för 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'
Ett annat alternativ är att använda funktionen pairs()
och filtrera bort icke-heltalindex:
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