Szukaj…


Wprowadzenie

Varargs , jak są powszechnie znane, umożliwiają funkcjom przyjmowanie dowolnej liczby argumentów bez specyfikacji. Wszystkie argumenty podane dla takiej funkcji są spakowane w pojedynczą strukturę znaną jako lista vararg ; który jest napisany jako ... w Lua. Istnieją podstawowe metody wyodrębniania liczby podanych argumentów i ich wartości za pomocą funkcji select() , ale bardziej zaawansowane wzorce użytkowania mogą wykorzystać strukturę do jej pełnej użyteczności.

Składnia

  • ... - Sprawia, że funkcja, której lista argumentów zawiera funkcję variadic
  • select (what, ...) - Jeśli „what” jest liczbą z zakresu 1 do liczby elementów w vararg, zwraca „what” element do ostatniego elementu w vararg. Zwrot będzie zero, jeśli indeks jest poza zakresem. Jeśli „what” jest ciągiem „#”, zwraca liczbę elementów w vararg.

Uwagi

Wydajność

Lista vararg jest implementowana jako lista połączona w implementacji języka PUC-Rio, co oznacza, że indeksy to O (n). Oznacza to, że iteracja elementów w vararg za pomocą select() , podobnie jak w poniższym przykładzie, jest operacją O (n ^ 2).

for i = 1, select('#', ...) do
    print(select(i, ...))
end

Jeśli planujesz iterację elementów na liście vararg, najpierw spakuj listę do tabeli. Dostęp do tabeli to O (1), więc iteracja to łącznie O (n). Lub, jeśli jesteś tak skłonny, zobacz przykład foldr() z sekcji zaawansowanych zastosowań; wykorzystuje iterację do iteracji po liście vararg w O (n).

Definicja długości sekwencji

Vararg jest użyteczny, ponieważ długość vararg uwzględnia wszelkie jawnie przekazane (lub obliczone) zera. Na przykład.

function test(...)
    return select('#', ...)
end

test()             --> 0
test(nil, 1, nil)  --> 3

To zachowanie jest jednak sprzeczne z zachowaniem tabel, w których operator długości # nie działa z „dziurami” (osadzonymi zerami) w sekwencjach. Obliczanie długości stołu z otworami jest niezdefiniowane i nie można na nim polegać. Tak więc, w zależności od wartości w ... , przyjmowanie długości {...} może nie dać „poprawnej” odpowiedzi. W Lua table.pack() wprowadzono table.pack() aby poradzić sobie z tym brakiem (w przykładzie jest funkcja, która implementuje tę funkcję w czystym Lua).

Zastosowanie idiomatyczne

Ponieważ varargs noszą na swojej długości ludzie używają ich jako sekwencji, aby uniknąć problemu z dziurami w tabelach. To nie było ich zamierzone użycie, a referencyjna implementacja Lua nie jest dla niego optymalna. Mimo że takie użycie jest badane w przykładach, ogólnie jest źle traktowane.

Podstawy

Funkcje variadic są tworzone przy użyciu składni ... elipsy na liście argumentów definicji funkcji.

function id(...)
    return
end

Jeśli nazwiesz tę funkcję jako id(1, 2, 3, 4, 5) to ... (AKA lista vararg) będzie zawierać wartości 1, 2, 3, 4, 5 .

Funkcje mogą przyjmować wymagane argumenty, a także ...

function head(x, ...)
    return x
end

Najłatwiejszym sposobem na pobranie elementów z listy vararg jest po prostu przypisanie do niej zmiennych.

function head3(...)
    local a, b, c = ...
    return a, b, c
end

select() może być również użyty do znalezienia liczby elementów i wyodrębnienia elementów z ... pośrednio.

function my_print(...)
    for i = 1, select('#', ...) do
        io.write(tostring(select(i, ...)) .. '\t')
    end
    io.write '\n'
end

... można spakować do stołu dla łatwości użycia, używając {...} . To umieszcza wszystkie argumenty w sekwencyjnej części tabeli.

5.2

table.pack(...) można również wykorzystać do spakowania listy vararg do tabeli. Zaletą table.pack(...) jest to, że ustawia pole n zwracanej tabeli na wartość select('#', ...) . Jest to ważne, jeśli lista argumentów może zawierać wartości zerowe (patrz sekcja uwag poniżej).

function my_tablepack(...)
    local t = {...}
    t.n = select('#', ...)
    return t
end

Lista vararg może być również zwrócona z funkcji. Wynikiem jest wiele zwrotów.

function all_or_none(...)
    local t = table.pack(...)
    for i = 1, t.n do
        if not t[i] then
            return    -- return none
        end
    end
    return ...    -- return all
end

Zaawansowane użycie

Jak podano w podstawowych przykładach, możesz mieć argumenty związane ze zmiennymi i listę argumentów zmiennych ( ... ). Możesz użyć tego faktu, aby rekurencyjnie rozdzielić listę, tak jak w innych językach (takich jak Haskell). Poniżej znajduje się implementacja foldr() która z tego korzysta. Każde wywołanie rekurencyjne wiąże nagłówek listy vararg do x i przekazuje resztę listy do wywołania rekurencyjnego. Spowoduje to destrukcję listy, dopóki nie będzie tylko jednego argumentu ( select('#', ...) == 0 ). Następnie każda wartość jest stosowana do argumentu funkcji f z wcześniej obliczonym wynikiem.

function foldr(f, ...)
    if select('#', ...) < 2 then return ... end
    local function helper(x, ...)
        if select('#', ...) == 0 then
          return x
        end
        return f(x, helper(...))
    end
    return helper(...)
end

function sum(a, b)
    return a + b
end

foldr(sum, 1, 2, 3, 4)
--> 10    

Inne definicje funkcji, które wykorzystują ten styl programowania, można znaleźć tutaj od numeru 3 do numeru 8.

Jedyną idiomatyczną strukturą danych Lui jest tabela. Operator długości tabeli jest niezdefiniowany, jeśli w dowolnym miejscu w sekwencji znajdują się nil . W przeciwieństwie tabelach lista vararg szanuje wyraźny nil s jak podano w podstawowych przykładów i uwag sekcję (proszę przeczytać tę sekcję, jeśli jeszcze nie). Przy niewielkim nakładzie lista vararg może wykonać każdą operację w tabeli oprócz mutacji. To sprawia, że lista vararg jest dobrym kandydatem do implementacji niezmiennych krotek.

function tuple(...)
    -- packages a vararg list into an easily passable value
    local co = coroutine.wrap(function(...)
        coroutine.yield()
        while true do
            coroutine.yield(...)
        end
    end)
    co(...)
    return co
end

local t = tuple((function() return 1, 2, nil, 4, 5 end)())

print(t())                 --> 1    2    nil    4    5    | easily unpack for multiple args
local a, b, d = t()        --> a = 1, b = 2, c = nil      | destructure the tuple
print((select(4, t())))    --> 4                          | index the tuple
print(select('#', t()))    --> 5                          | find the tuple arity (nil respecting)

local function change_index(tpl, i, v)
    -- sets a value at an index in a tuple (non-mutating)
    local function helper(n, x, ...)
        if select('#', ...) == 0 then
            if n == i then
                return v
            else
                return x
            end
        else
            if n == i then
                return v, helper(n+1, ...)
            else
                return x, helper(n+1, ...)
            end
        end
    end
    return tuple(helper(1, tpl()))
end

local n = change_index(t, 3, 3)
print(t())                 --> 1    2    nil    4    5
print(n())                 --> 1    2    3    4    5

Główną różnicą między powyższymi tabelami a tabelami jest to, że tabele są zmienne i mają semantykę wskaźnika, przy czym krotka nie ma tych właściwości. Dodatkowo krotki mogą zawierać jawne nil i mieć operację o nieokreślonej długości.



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