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 znalezionegonil
. - Podczas korzystania z funkcji
ipairs()
ponieważ, jak wspomniano powyżej, przestaje iterować od pierwszego znalezionegonil
. - Podczas korzystania z funkcji
table.unpack()
ponieważ ta metoda zatrzymuje rozpakowywanie przy pierwszym znalezionymnil
. - 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
- 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ć). - Przed wstawieniem sprawdź wartości
nil
, unikając takich rzeczy jaktable.pack(function_call())
, które mogą zakraść wartościnil
do naszej tabeli. - Po wstawieniu sprawdź wartości
nil
i, jeśli to konieczne, wypełnij lukę, przesuwając wszystkie kolejne wartości. - Jeśli to możliwe, użyj wartości symboli zastępczych. Na przykład zmień
nil
na0
lub inną wartość zastępczą. - Jeżeli pozostawienie luk jest nieuniknione, należy to odpowiednio udokumentować (skomentować).
- 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