Zoeken…
Syntaxis
- ipairs (numerieke_tabel) - Lua-tabel met iterator met numerieke indices
- paren (input_table) - generieke Lua-tabeliterator
- key, value = next (input_table, input_key) - Lua tabel waardekiezer
- table.insert (input_table, [position], value) - voer de opgegeven waarde in de invoertabel in
- remove_value = table.remove (input_table, [position]) - knal als laatste of verwijder de waarde gespecificeerd door position
Opmerkingen
Tabellen zijn de enige ingebouwde datastructuur die beschikbaar is in Lua. Dit is elegante eenvoud of verwarrend, afhankelijk van hoe je het bekijkt.
Een Lua-tabel is een verzameling sleutel / waarde-paren waarbij de sleutels uniek zijn en noch de sleutel, noch de waarde nil
. Als zodanig kan een Lua-tabel lijken op een woordenboek, hashmap of associatieve array uit andere talen. Veel structurele patronen kunnen worden gebouwd met tabellen: stapels, wachtrijen, sets, lijsten, grafieken, enz. Ten slotte kunnen tabellen worden gebruikt om klassen in Lua te bouwen en een modulesysteem te maken.
Lua hanteert geen specifieke regels voor het gebruik van tabellen. De items in een tabel kunnen een combinatie van Lua-typen zijn. Eén tabel kan dus bijvoorbeeld tekenreeksen, functies, booleans, getallen en zelfs andere tabellen als waarden of toetsen bevatten.
Van een Lua-tabel met opeenvolgende positieve gehele getallen die met 1 beginnen, wordt gezegd dat deze een reeks heeft. De sleutel / waarde-paren met positieve geheeltallige sleutels zijn de elementen van de reeks. Andere talen noemen dit een op 1 gebaseerde array. Bepaalde standaardbewerkingen en functies werken alleen op de volgorde van een tabel en sommige hebben niet-deterministisch gedrag wanneer ze worden toegepast op een tabel zonder een reeks.
Als u een waarde in een tabel op nil
wordt deze uit de tabel verwijderd. Iterators zouden de bijbehorende sleutel niet langer zien. Wanneer u codeert voor een tabel met een reeks, is het belangrijk om de reeks niet te breken; Verwijder alleen het laatste element of gebruik een functie, zoals de standaard table.remove
, dat verschuivingen elementen naar het dichten.
Tabellen maken
Een lege tabel maken is zo eenvoudig als dit:
local empty_table = {}
U kunt ook een tabel maken in de vorm van een eenvoudige array:
local numeric_table = {
"Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.
Houd er rekening mee dat het indexeren van tabellen standaard begint bij 1.
Het is ook mogelijk om een tabel met associatieve elementen te maken:
local conf_table = {
hostname = "localhost",
port = 22,
flags = "-Wall -Wextra"
clients = { -- nested table
"Eve", "Jim", "Peter"
}
}
Het bovenstaande gebruik is syntaxisuiker voor wat hieronder staat. De sleutels in dit geval zijn van het type string. De bovenstaande syntaxis is toegevoegd om tabellen als records weer te geven. Deze syntaxis in recordstijl loopt parallel met de syntaxis voor indexeringstabellen met string-toetsen, zoals te zien in de tutorial 'basisgebruik'.
Zoals uitgelegd in het gedeelte met opmerkingen, werkt de syntaxis van de recordstijl niet voor elke mogelijke sleutel. Bovendien kan een sleutel elke waarde van elk type hebben, en de vorige voorbeelden hadden alleen betrekking op tekenreeksen en opeenvolgende getallen. In andere gevallen moet u de expliciete syntaxis gebruiken:
local unique_key = {}
local ops_table = {
[unique_key] = "I'm unique!"
["^"] = "power",
[true] = true
}
Tabellen herhalen
De standaardbibliotheek van Lua biedt een pairs
die de toetsen en waarden van een tabel doorloopt. Bij het itereren met pairs
er geen gespecificeerde volgorde voor doorkruisen, zelfs als de toetsen van de tabel numeriek zijn .
for key, value in pairs(input_table) do
print(key, " -- ", value)
end
Voor tabellen die numerieke toetsen gebruiken , biedt Lua een ipairs
functie. De functie ipairs
zal altijd doorgaan van table[1]
, table[2]
, enz. Totdat de eerste nil
is gevonden.
for index, value in ipairs(numeric_table) do
print(index, ". ", value)
end
Wees gewaarschuwd dat iteratie met behulp van ipairs()
in enkele gevallen niet werkt zoals u misschien wilt:
input_table
heeft "gaten" erin. (Zie het gedeelte over "Vermijden van gaten in tabellen die als arrays worden gebruikt" voor meer informatie.) Bijvoorbeeld:table_with_holes = {[1] = "value_1", [3] = "value_3"}
toetsen waren niet allemaal numeriek. Bijvoorbeeld:
mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
Natuurlijk werkt het volgende ook voor een tabel die de juiste volgorde heeft:
for i = 1, #numeric_table do
print(i, ". ", numeric_table[i])
end
Een numerieke tabel in omgekeerde volgorde herhalen is eenvoudig:
for i = #numeric_table, 1, -1 do
print(i, ". ", numeric_table[i])
end
Een laatste manier om door tabellen te bladeren, is door de next
selector in een generieke for
lus te gebruiken . Net als pairs
er geen opgegeven volgorde voor doorkruisen. (De pairs
methode gebruikt next
intern. Dus het gebruik van next
is in wezen een meer handmatige versie van pairs
. Zie pairs
in Lua's referentiehandleiding en de next
in Lua's referentiehandleiding voor meer details.)
for key, value in next, input_table do
print(key, value)
end
Basisgebruik
Het basisgebruik van tabellen omvat het openen en toewijzen van tabelelementen, het toevoegen van tabelinhoud en het verwijderen van tabelinhoud. In deze voorbeelden wordt ervan uitgegaan dat u weet hoe u tabellen kunt maken.
Toegang tot elementen
Gezien de volgende tabel,
local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
"Diarrhea", cure = "Pepto Bismol"}
Men kan het sequentiële deel van de tabel indexeren met behulp van de indexsyntaxis, waarbij het argument voor de indexsyntaxis de sleutel is van het gewenste sleutel / waarde-paar. Zoals uitgelegd in de creatie-tutorial, is de meeste declaratiesyntaxis syntactische suiker voor het declareren van sleutel / waarde-paren. Achtereenvolgens opgenomen elementen, zoals de eerste vijf waarden in example_table
, gebruiken toenemende gehele waarden als sleutels; de recordsyntaxis gebruikt de naam van het veld als een tekenreeks.
print(example_table[2]) --> Heartburn
print(example_table["cure"]) --> Pepto Bismol
Voor tekenreekstoetsen is er syntaxisuiker om de syntaxis van de recordstijl voor tekenreekstoetsen parallel te maken bij het maken van tabellen. De volgende twee regels zijn equivalent.
print(example_table.cure) --> Pepto Bismol
print(example_table["cure"]) --> Pepto Bismol
U kunt toegang krijgen tot tabellen met sleutels die u nog niet eerder hebt gebruikt, dat is geen fout zoals in andere talen. Als u dit doet, wordt de standaardwaarde nil
geretourneerd.
Elementen toewijzen
U kunt bestaande tabelelementen wijzigen door aan een tabel toe te wijzen met behulp van de indexsyntaxis. Bovendien is de index-syntaxis in recordstijl beschikbaar voor het instellen van waarden
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
U kunt ook nieuwe elementen aan een bestaande tabel toevoegen met behulp van een opdracht.
example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"
Speciale opmerking: sommige tekenreeksen worden niet ondersteund met de record-syntaxis. Zie het opmerkingen gedeelte voor details.
Elementen verwijderen
Zoals eerder vermeld, is de standaardwaarde voor een sleutel zonder toegewezen waarde nil
. Een element uit een tabel verwijderen is net zo eenvoudig als het terugzetten van de waarde van een sleutel naar de standaardwaarde.
example_table[100] = "Face Mask"
De elementen zijn nu niet te onderscheiden van een niet-ingesteld element.
Tafel lengte
Tabellen zijn eenvoudig associatieve arrays (zie opmerkingen), maar wanneer aaneengesloten gehele getallen worden gebruikt, beginnend vanaf 1, wordt gezegd dat de tabel een reeks heeft .
Het vinden van de lengte van het sequentiedeel van een tabel wordt gedaan met #
:
local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table) --> 8
U kunt de lengtebewerking gebruiken om eenvoudig items aan een sequentietabel toe te voegen.
example_table[#example_table+1] = 'a'
print(#example_table) --> 9
In het bovenstaande voorbeeld is de vorige waarde van #example_table
8
, als u 1
toevoegt, krijgt u de volgende geldige gehele sleutel in de reeks, 9
, dus ... example_table[9] = 'a'
. Dit werkt voor elke lengte van de tafel.
Speciale opmerking: het gebruik van geheeltallige toetsen die niet aaneengesloten zijn en begint bij 1, breekt de reeks waardoor de tabel een schaarse tabel wordt . Het resultaat van de lengtebewerking is in dat geval niet gedefinieerd. Zie het opmerkingen gedeelte.
Tabelbibliotheekfuncties gebruiken om elementen toe te voegen / te verwijderen
Een andere manier om elementen aan een tabel toe te voegen, is de functie table.insert()
. De invoegfunctie werkt alleen op sequentietabellen. Er zijn twee manieren om de functie aan te roepen. Het eerste voorbeeld toont het eerste gebruik, waarbij men de index specificeert om het element in te voegen (het tweede argument). Dit duwt alle elementen uit de gegeven index naar #table
één positie omhoog. Het tweede voorbeeld toont het andere gebruik van table.insert()
, waarbij de index niet is opgegeven en de opgegeven waarde aan het einde van de tabel is toegevoegd (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"}
Om table.insert()
voor het verwijderen van elementen parallel te table.insert()
is table.remove()
. Op dezelfde manier heeft het twee roepende semantiek: een voor het verwijderen van elementen op een gegeven positie en een andere voor het verwijderen van het einde van de reeks. Bij het verwijderen uit het midden van een reeks, worden alle volgende elementen één index omlaag verplaatst.
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"
Deze twee functies muteren de gegeven tabel. Zoals u wellicht kunt zien, biedt de tweede methode voor het aanroepen van table.insert()
en table.remove()
stack-semantiek voor tabellen. Gebruikmakend van dat, kunt u code schrijven zoals in het onderstaande voorbeeld.
function shuffle(t)
for i = 0, #t-1 do
table.insert(t, table.remove(t, math.random(#t-i)))
end
end
Het implementeert de Fisher-Yates Shuffle, misschien inefficiënt. Het gebruikt de table.insert()
om het willekeurig geëxtraheerde element aan het einde van dezelfde tabel toe te voegen en de table.remove()
om een element willekeurig uit het resterende niet-geschudde deel van de tabel te extraheren.
Vermijden van gaten in tabellen die als arrays worden gebruikt
Onze voorwaarden definiëren
Met array bedoelen we een Lua-tabel die als een reeks wordt gebruikt. Bijvoorbeeld:
-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}
We gebruiken deze tabel als een reeks: een groep items die worden getoetst door gehele getallen. Veel talen noemen dit een array, en wij ook. Maar strikt genomen bestaat er in Lua niet zoiets als een array. Er zijn alleen tabellen, waarvan sommige array-achtig zijn, sommige hash-achtig (of woordenboekachtig, als je dat liever hebt), en sommige zijn gemengd.
Een belangrijk punt van onze reeks pets
is dat er geen gaten zijn. Het eerste item, pets[1]
, is de string "honden", het tweede item, pets[2]
, is de string "katten", en het laatste item, pets[3]
, is "vogels". Lua's standaardbibliotheek en de meeste modules geschreven voor Lua gaan uit van 1 als de eerste index voor reeksen. Een gapless array bevat daarom items van 1..n
zonder nummers in de reeks te missen. (In het beperkende geval is n = 1
en bevat de array slechts één item.)
Lua biedt de ingebouwde functie ipairs
om dergelijke tabellen te ipairs
.
-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Dit zou afdrukken "Item op positie 1 is honden.", "Item op positie 2 is katten.", "Item op positie 3 is vogels."
Maar wat gebeurt er als we het volgende doen?
local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
print("Item at position " .. idx .. " is " .. pet .. ".")
end
Een array zoals dit tweede voorbeeld is een schaarse array. Er zitten gaten in de reeks. Deze array ziet er zo uit:
{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1 2 3 4 5 6 7 8 9 10 11 12
De nulwaarden nemen geen extra geheugen in beslag; intern bewaart lua alleen de waarden [1] = "dogs"
, [2] = "cats"
, [3] = "birtds"
en [12] = "goldfish"
Om de directe vraag te beantwoorden, zullen ipairs
stoppen na vogels; "goudvissen" bij pets[12]
worden nooit bereikt tenzij we onze code aanpassen. Dit komt omdat ipairs
itereert van 1..n-1
waarbij n
de positie is van de eerste gevonden nil
. Lua definieert table[length-of-table + 1]
als nil
. Dus in een juiste volgorde stopt iteratie wanneer Lua bijvoorbeeld het vierde item in een array met drie items probeert te krijgen.
Wanneer?
De twee meest voorkomende plaatsen voor problemen met schaarse arrays zijn (i) wanneer u probeert de lengte van de array te bepalen en (ii) wanneer u probeert over de array te itereren. Met name:
- Wanneer u de operator
#
length gebruikt, omdat de operator length stopt met tellen bij de eerste gevondennil
. - Wanneer de functie
ipairs()
gebruikt, stopt het, zoals hierboven vermeld, bij de eerste gevondennil
. - Bij gebruik van de functie
table.unpack()
omdat deze methode stopt met uitpakken bij de eerste gevondennil
. - Wanneer u andere functies gebruikt die (direct of indirect) toegang hebben tot een van de bovenstaande functies.
Om dit probleem te voorkomen, is het belangrijk om uw code te schrijven, zodat u geen gaten invoert als u verwacht dat een tabel een array is. Hiaten kunnen op verschillende manieren worden aangebracht:
- Als u iets op een verkeerde positie aan een array toevoegt.
- Als u een
nil
in een array invoegt. - Als u waarden uit een array verwijdert.
Je zou kunnen denken: "Maar ik zou nooit iets van die dingen doen." Nou, niet opzettelijk, maar hier is een concreet voorbeeld van hoe dingen mis kunnen gaan. Stel je voor dat je een filtermethode voor Lua wilt schrijven, zoals Ruby's select
en Perl's grep
. De methode accepteert een testfunctie en een array. Het itereert over de array en roept om de beurt de testmethode op elk item aan. Als het item slaagt, wordt dat item toegevoegd aan een resultatenmatrix die aan het einde van de methode wordt geretourneerd. Het volgende is een buggy-implementatie:
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
Het probleem is dat wanneer de functie false
retourneert, we een getal in de reeks overslaan. Stel je voor dat je een filter(isodd, {1,2,3,4,5,6,7,8,9,10})
: er zijn gaten in de geretourneerde tabel elke keer dat er een even getal in de array wordt doorgegeven om te filter
.
Hier is een vaste implementatie:
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
- Gebruik standaardfuncties:
table.insert(<table>, <value>)
altijd toegevoegd aan het einde van de array.table[#table + 1] = value
is hiervoor een korte hand.table.remove(<table>, <index>)
verplaatst alle volgende waarden terug om het gat te vullen (wat het ook traag kan maken). - Controleer op
nil
waarden voor het plaatsen, het vermijden van dingen alstable.pack(function_call())
, wat kan sluipennil
waarden in onze tafel. - Controleer op
nil
waarden na het invoegen en eventueel vullen van het gat door het verschuiven van alle opeenvolgende waarden. - Gebruik indien mogelijk waarden voor tijdelijke aanduiding. Wijzig bijvoorbeeld
nil
voor0
of een andere tijdelijke aanduiding-waarde. - Als hiaten onvermijdelijk zijn, moet dit goed worden gedocumenteerd (becommentarieerd).
- Schrijf een
__len()
metamethod en gebruik de operator#
.
Voorbeeld voor 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'
Een ander alternatief is om de pairs()
-functie te gebruiken en de niet-gehele indices uit te filteren:
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