Szukaj…


Składnia

  • ipairs (numeric_table) - Tabela Lua z iteratorem indeksów numerycznych
  • pairs (input_table) - ogólny iterator tabeli Lua
  • key, value = next (input_table, input_key) - Selektor wartości tabeli Lua
  • table.insert (tabela_wejściowa, [pozycja], wartość) - wstaw określoną wartość do tabeli wejściowej
  • remove_value = table.remove (input_table, [position]) - pop last lub usuń wartość określoną przez position

Uwagi

Tabele to jedyna wbudowana struktura danych dostępna w Lua. Jest to albo elegancka prostota, albo myląca, w zależności od tego, jak na to patrzysz.

Tabela Lua to zbiór par klucz-wartość, w których klucze są unikalne, a ani klucz, ani wartość nil jest równa nil . W związku z tym tabela Lua może przypominać słownik, tablicę skrótów lub tablicę asocjacyjną z innych języków. Za pomocą tabel można budować wiele wzorów strukturalnych: stosy, kolejki, zestawy, listy, wykresy itp. Na koniec można użyć tabel do budowania klas w Lua i tworzenia systemu modułów .

Lua nie egzekwuje żadnych szczególnych zasad dotyczących używania tabel. Elementy zawarte w tabeli mogą być mieszanką typów Lua. Na przykład jedna tabela może zawierać ciągi znaków, funkcje, wartości logiczne, liczby, a nawet inne tabele jako wartości lub klucze.

Mówi się, że tabela Lua z kolejnymi dodatnimi kluczami całkowitymi rozpoczynającymi się od 1 ma sekwencję. Pary klucz-wartość z dodatnimi liczbami całkowitymi są elementami sekwencji. Inne języki nazywają to tablicą opartą na 1. Niektóre standardowe operacje i funkcje działają tylko na sekwencji tabeli, a niektóre zachowują się w sposób niedeterministyczny po zastosowaniu do tabeli bez sekwencji.

Ustawienie wartości w tabeli na nil powoduje usunięcie jej z tabeli. Iteratory nie będą już widzieć powiązanego klucza. Podczas kodowania tabeli z sekwencją ważne jest, aby unikać przerywania sekwencji; Usuń tylko ostatni element lub użyj funkcji, takiej jak standardowy table.remove , która przesuwa elementy w dół, aby zamknąć odstęp.

Tworzenie tabel

Utworzenie pustej tabeli jest tak proste:

local empty_table = {}

Możesz także utworzyć tabelę w postaci prostej tablicy:

local numeric_table = {
    "Eve", "Jim", "Peter"
}
-- numeric_table[1] is automatically "Eve", numeric_table[2] is "Jim", etc.

Pamiętaj, że domyślnie indeksowanie tabel rozpoczyna się od 1.

Możliwe jest również utworzenie tabeli z elementami asocjacyjnymi:

local conf_table = {
    hostname = "localhost",
    port     = 22,
    flags    = "-Wall -Wextra"
    clients  = {                -- nested table
        "Eve", "Jim", "Peter"
    }
}

Powyższe użycie to cukier składniowy do tego, co jest poniżej. Klucze w tym przypadku są typu ciąg. Powyższa składnia została dodana, aby tabele były wyświetlane jako rekordy. Ta składnia w stylu rekordu jest równoległa ze składnią indeksowania tabel za pomocą kluczy łańcuchowych, jak pokazano w samouczku „Podstawowego użytkowania”.

Jak wyjaśniono w sekcji uwag, składnia w stylu rekordu nie działa dla każdego możliwego klawisza. Dodatkowo kluczem może być dowolna wartość dowolnego typu, a poprzednie przykłady obejmowały tylko ciągi znaków i kolejne numery. W innych przypadkach musisz użyć jawnej składni:

local unique_key = {}
local ops_table = {
    [unique_key] = "I'm unique!"
    ["^"]  = "power",
    [true] = true
}

Stoły iteracyjne

Standardowa biblioteka Lua udostępnia funkcję pairs która iteruje klucze i wartości tabeli. Podczas iteracji pairs nie ma określonej kolejności przechodzenia, nawet jeśli klawisze tabeli są numeryczne .

for key, value in pairs(input_table) do
    print(key, " -- ", value)
end

W przypadku tabel używających klawiszy numerycznych Lua zapewnia funkcję ipairs . Funkcja ipairs będzie zawsze iterować od table[1] , table[2] itd., Aż do znalezienia pierwszej wartości nil .

for index, value in ipairs(numeric_table) do
    print(index, ". ", value)
end

Ostrzegamy, że iteracja przy użyciu ipairs() nie będzie działać tak, jak chcesz w kilku przypadkach:

  • input_table ma w sobie „dziury”. (Aby uzyskać więcej informacji, zobacz sekcję „Unikanie luk w tabelach używanych jako tablice”). Na przykład:

    table_with_holes = {[1] = "value_1", [3] = "value_3"}
    
  • klucze nie były wszystkie numeryczne. Na przykład:

    mixed_table = {[1] = "value_1", ["not_numeric_index"] = "value_2"}
    

Oczywiście, poniższe czynności działają również dla tabeli, która jest prawidłową sekwencją:

for i = 1, #numeric_table do
    print(i, ". ", numeric_table[i])
end

Iterowanie tabeli numerycznej w odwrotnej kolejności jest łatwe:

for i = #numeric_table, 1, -1 do
    print(i, ". ", numeric_table[i])
end

Ostatnim sposobem na iterację po tabelach jest użycie next selektora w ogólnej pętli for . Podobnie jak w pairs nie ma określonej kolejności przejścia. (Metoda pairs używa next metody wewnętrznej. Zatem użycie next jest zasadniczo bardziej ręczną wersją pairs . Zobacz pairs w podręczniku Lua i next w podręczniku Lua, aby uzyskać więcej szczegółów.)

for key, value in next, input_table do
    print(key, value)
end

Podstawowe użycie

Podstawowe użycie tabeli obejmuje dostęp i przypisywanie elementów tabeli, dodawanie zawartości tabeli i usuwanie zawartości tabeli. Te przykłady zakładają, że wiesz, jak tworzyć tabele.

Dostęp do elementów

Biorąc pod uwagę poniższą tabelę,

local example_table = {"Nausea", "Heartburn", "Indigestion", "Upset Stomach",
                       "Diarrhea", cure = "Pepto Bismol"}

Można indeksować sekwencyjną część tabeli za pomocą składni indeksu, argumentem składni indeksu jest klucz żądanej pary klucz-wartość. Jak wyjaśniono w samouczku tworzenia, większość składni deklaracji to cukier składniowy do deklarowania par klucz-wartość. Elementy włączone sekwencyjnie, takie jak pierwsze pięć wartości w example_table , używają rosnących wartości całkowitych jako kluczy; składnia rekordu używa nazwy pola jako łańcucha.

print(example_table[2])        --> Heartburn
print(example_table["cure"])   --> Pepto Bismol

W przypadku kluczy łańcuchowych istnieje cukier równoległy do składni w stylu rekordu dla kluczy łańcuchowych podczas tworzenia tabeli. Poniższe dwie linie są równoważne.

print(example_table.cure)      --> Pepto Bismol
print(example_table["cure"])   --> Pepto Bismol

Możesz uzyskać dostęp do tabel za pomocą kluczy, których wcześniej nie używałeś, co nie jest błędem, jak w innych językach. Spowoduje to zwrócenie domyślnej wartości nil .

Przypisywanie elementów

Można modyfikować istniejące elementy tabeli, przypisując do tabeli przy użyciu składni indeksu. Dodatkowo dostępna jest również składnia indeksowania w stylu rekordu do ustawiania wartości

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

Możesz także dodać nowe elementy do istniejącej tabeli za pomocą przypisania.

example_table.copyright_holder = "Procter & Gamble"
example_table[100] = "Emergency source of water"

Uwaga specjalna: Niektóre ciągi znaków nie są obsługiwane przez składnię rekordów. Szczegółowe informacje można znaleźć w sekcji uwag.

Usuwanie elementów

Jak wspomniano wcześniej, domyślna wartość klucza bez przypisanej wartości wynosi nil . Usunięcie elementu z tabeli jest tak proste, jak zresetowanie wartości klucza do wartości domyślnej.

example_table[100] = "Face Mask"

Elementy są teraz nie do odróżnienia od elementu nieuzbrojonego.

Długość stołu

Tabele są po prostu tablicami asocjacyjnymi (patrz uwagi), ale gdy używane są ciągłe liczby całkowite, począwszy od 1, mówi się, że tabela ma sekwencję .

Znalezienie długości części sekwencji w tabeli odbywa się za pomocą # :

local example_table = {'a', 'l', 'p', 'h', 'a', 'b', 'e', 't'}
print(#example_table)    --> 8

Możesz użyć operacji długości, aby łatwo dołączyć elementy do tabeli sekwencji.

example_table[#example_table+1] = 'a'
print(#example_table)    --> 9

W powyższym przykładzie poprzednia wartość #example_table wynosi 8 , dodanie 1 daje następny prawidłowy klucz liczby całkowitej w sekwencji, 9 , więc ... example_table[9] = 'a' . Działa to dla dowolnej długości stołu.

Uwaga specjalna: Użycie kluczy całkowitych, które nie są ciągłe i rozpoczynając od 1, przerywa sekwencję, czyniąc tabelę tabelą rzadką . W takim przypadku wynik operacji długości jest niezdefiniowany. Zobacz sekcję uwag.

Używanie funkcji biblioteki tabel do dodawania / usuwania elementów

Innym sposobem dodawania elementów do tabeli jest funkcja table.insert() . Funkcja wstawiania działa tylko na tabelach sekwencji. Istnieją dwa sposoby wywołania tej funkcji. Pierwszy przykład pokazuje pierwsze użycie, w którym określa się indeks wstawienia elementu (drugi argument). #table to wszystkie elementy z podanego indeksu do #table górę o jedną pozycję. Drugi przykład pokazuje inne użycie table.insert() , w którym indeks nie jest określony, a podana wartość jest dołączana na końcu tabeli (indeks #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"}

Do równoległego table.insert() do usuwania elementów służy table.remove() . Podobnie ma dwie wywołujące semantykę: jedną do usuwania elementów w danej pozycji, a drugą do usuwania z końca sekwencji. Podczas usuwania ze środka sekwencji wszystkie następujące elementy są przesuwane w dół o jeden indeks.

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"

Te dwie funkcje mutują podaną tabelę. Jak możesz być w stanie powiedzieć, druga metoda wywoływania table.insert() i table.remove() zapewnia semantykę stosu do tabel. Wykorzystując to, możesz napisać kod jak w poniższym przykładzie.

function shuffle(t)
    for i = 0, #t-1 do
        table.insert(t, table.remove(t, math.random(#t-i)))
    end
end

Wprowadza losowe losowanie Fishera-Yatesa, być może nieefektywnie. Wykorzystuje table.insert() aby dołączyć losowo wyodrębniony element na końcu tej samej tabeli, a table.remove() aby losowo wyodrębnić element z pozostałej niezasadzonej części tabeli.

Unikanie luk w tabelach używanych jako tablice

Definiowanie naszych warunków

Przez tablicę rozumiemy tutaj tabelę Lua używaną jako sekwencja. Na przykład:

-- Create a table to store the types of pets we like.
local pets = {"dogs", "cats", "birds"}

Używamy tej tabeli jako sekwencji: grupy elementów wpisanych w liczby całkowite. Wiele języków nazywa to tablicą, podobnie jak my. Ale ściśle mówiąc, nie ma czegoś takiego jak tablica w Lua. Istnieją tylko tabele, z których niektóre są podobne do tablic, niektóre z nich przypominają hasz (lub słownik, jeśli wolisz), a niektóre są mieszane.

Ważną kwestią dotyczącą naszej gamy pets jest to, że nie ma żadnych luk. Pierwszy element, pets[1] , to ciąg „psy”, drugi element, pets[2] , to ciąg „koty”, a ostatni element, pets[3] , to „ptaki”. Standardowa biblioteka Lua i większość modułów napisanych dla Lua przyjmuje 1 jako pierwszy indeks sekwencji. Dlatego tablica bez przerw zawiera elementy od 1..n bez brakujących liczb w sekwencji. (W ograniczającym przypadku n = 1 , a tablica zawiera tylko jeden element).

Lua zapewnia wbudowaną funkcję ipairs do iteracji po takich tabelach.

-- Iterate over our pet types.
for idx, pet in ipairs(pets) do
  print("Item at position " .. idx .. " is " .. pet .. ".")
end

Spowoduje to wydrukowanie „Przedmiotem na pozycji 1 są psy.”, „Przedmiotem na pozycji 2 są koty.”, „Przedmiotem na pozycji 3 są ptaki”.

Ale co się stanie, jeśli wykonamy następujące czynności?

local pets = {"dogs", "cats", "birds"}
pets[12] = "goldfish"
for idx, pet in ipairs(pets) do
  print("Item at position " .. idx .. " is " .. pet .. ".")
end

Tablica taka jak ten drugi przykład jest tablicą rzadką. W sekwencji są luki. Ta tablica wygląda następująco:

{"dogs", "cats", "birds", nil, nil, nil, nil, nil, nil, nil, nil, "goldfish"}
-- 1        2       3      4    5    6    7    8    9    10   11       12     

Wartości zerowe nie zajmują dodatkowej pamięci; wewnętrznie lua zapisuje tylko wartości [1] = "dogs" , [2] = "cats" , [3] = "birtds" i [12] = "goldfish"

Aby odpowiedzieć na bezpośrednie pytanie, ipairs zatrzymają się po ptakach; „złota rybka” u pets[12] nigdy nie zostanie osiągnięta, dopóki nie dostosujemy naszego kodu. Jest tak, ponieważ ipairs iteruje od 1..n-1 gdzie n jest pozycją pierwszego znalezionego nil . Lua definiuje table[length-of-table + 1] na nil . Tak więc w odpowiedniej kolejności iteracja zatrzymuje się, gdy Lua próbuje uzyskać, powiedzmy, czwarty element w tablicy trzech elementów.

Gdy?

Dwa najczęstsze miejsca występowania problemów z rzadkimi tablicami to (i) podczas próby ustalenia długości tablicy i (ii) podczas próby iteracji po tablicy. W szczególności:

  • Podczas korzystania z operatora # length, ponieważ operator długości przestaje liczyć od pierwszego znalezionego nil .
  • Podczas korzystania z funkcji ipairs() ponieważ, jak wspomniano powyżej, przestaje iterować od pierwszego znalezionego nil .
  • Podczas korzystania z funkcji table.unpack() ponieważ ta metoda zatrzymuje rozpakowywanie przy pierwszym znalezionym nil .
  • Podczas korzystania z innych funkcji, które (bezpośrednio lub pośrednio) uzyskują dostęp do któregokolwiek z powyższych.

Aby uniknąć tego problemu, ważne jest, aby napisać kod, aby jeśli oczekujesz, że tabela będzie tablicą, nie wprowadzaj luk. Luki można wprowadzić na kilka sposobów:

  • Jeśli dodasz coś do tablicy w niewłaściwej pozycji.
  • Jeśli wstawisz wartość nil do tablicy.
  • Jeśli usuniesz wartości z tablicy.

Możesz pomyśleć: „Ale nigdy nie zrobiłbym żadnej z tych rzeczy”. Cóż, nie celowo, ale oto konkretny przykład tego, jak coś może pójść nie tak. Wyobraź sobie, że chcesz napisać metodę filtrowania dla Lua jak Ruby select i Perl grep . Metoda zaakceptuje funkcję testową i tablicę. Iteruje po tablicy, wywołując kolejno metodę testową dla każdego elementu. Jeśli element przejdzie, wówczas zostanie dodany do tablicy wyników, która jest zwracana na końcu metody. Poniżej przedstawiono błędną implementację:

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

Problem polega na tym, że gdy funkcja zwraca false , pomijamy liczbę w sekwencji. Wyobraź sobie wywoływanie filter(isodd, {1,2,3,4,5,6,7,8,9,10}) : w zwróconej tabeli pojawią się luki za każdym razem, gdy w tablicy przekazywanej do filter pojawi się liczba parzysta.

Oto stała implementacja:

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

Porady

  1. Użyj standardowych funkcji: table.insert(<table>, <value>) zawsze dołącza się na końcu tablicy. table[#table + 1] = value jest do tego krótką ręką. table.remove(<table>, <index>) przeniesie wszystkie następujące wartości z powrotem, aby wypełnić lukę (co może również spowolnić).
  2. Przed wstawieniem sprawdź wartości nil , unikając takich rzeczy jak table.pack(function_call()) , które mogą zakraść wartości nil do naszej tabeli.
  3. Po wstawieniu sprawdź wartości nil i, jeśli to konieczne, wypełnij lukę, przesuwając wszystkie kolejne wartości.
  4. Jeśli to możliwe, użyj wartości symboli zastępczych. Na przykład zmień nil na 0 lub inną wartość zastępczą.
  5. Jeżeli pozostawienie luk jest nieuniknione, należy to odpowiednio udokumentować (skomentować).
  6. Napisz __len() i użyj operatora # .

Przykład dla 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'

Inną alternatywą jest użycie funkcji pairs() i odfiltrowanie wskaźników niecałkowitych:

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
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow