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örsta nil hittades.
  • När du använder ipairs() eftersom den som nämnts ovan slutar att iterera vid den första nil hittades.
  • När du använder table.unpack() eftersom denna metod slutar packa upp vid den första nil 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

  1. 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).
  2. Se efter nil innan du sätter i och undvik saker som table.pack(function_call()) , som kan smyga nil i vårt bord.
  3. Kontrollera efter nil efter infogning och fyll vid behov på mellanrummet genom att växla alla på varandra följande värden.
  4. Använd om möjligt värden för platshållare. Ändra till exempel nil för 0 eller något annat platshållarvärde.
  5. Om det är oundvikligt att lämna luckor, bör detta dokumenteras (kommenteras).
  6. 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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow