Suche…


Einführung

Varargs , wie sie allgemein bekannt sind, erlauben Funktionen, eine beliebige Anzahl von Argumenten ohne Angabe zu übernehmen. Alle Argumente, die einer solchen Funktion übergeben werden, werden in einer einzigen Struktur zusammengefasst, die als Vararg-Liste bezeichnet wird . was geschrieben wird als ... in Lua. Es gibt grundlegende Methoden, um die Anzahl der angegebenen Argumente und den Wert dieser Argumente mithilfe der Funktion select() zu extrahieren. Erweiterte Benutzungsmuster können die Struktur jedoch zu ihrem vollen Nutzen nutzen.

Syntax

  • ... - Macht die Funktion, deren Liste der Argumente eine Variadic-Funktion ist
  • select (what, ...) - Wenn "was" eine Zahl im Bereich 1 der Anzahl der Elemente im vararg ist, wird das Element "what" bis zum letzten Element im vararg zurückgegeben. Die Rückgabe ist null, wenn der Index außerhalb der Grenzen liegt. Wenn 'was' die Zeichenfolge '#' ist, wird die Anzahl der Elemente im vararg zurückgegeben.

Bemerkungen

Effizienz

Die Vararg-Liste wird als verknüpfte Liste in der PUC-Rio-Implementierung der Sprache implementiert. Dies bedeutet, dass die Indizes O (n) sind. Das bedeutet, dass das Durchlaufen der Elemente in einem vararg mithilfe von select() , wie im Beispiel unten, eine O (n ^ 2) -Operation ist.

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

Wenn Sie die Elemente in einer Vararg-Liste durchlaufen möchten, packen Sie die Liste zunächst in eine Tabelle. Tabellenzugriffe sind O (1), so dass insgesamt 0 (n) iteriert wird. Wenn Sie dazu neigen, foldr() Sie sich das Beispiel foldr() aus dem Abschnitt Advanced foldr() . Es verwendet Rekursion, um eine Vararg-Liste in O (n) zu durchlaufen.

Sequenzlänge Definition

Der vararg ist insofern nützlich, als die Länge des vararg alle explizit übergebenen (oder berechneten) Nils berücksichtigt. Zum Beispiel.

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

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

Dieses Verhalten steht jedoch im Konflikt mit dem Verhalten von Tabellen, bei denen der Längenoperator # nicht mit 'Löchern' (eingebetteten Nils) in Sequenzen arbeitet. Die Berechnung der Länge einer Tabelle mit Löchern ist undefiniert und kann nicht verlässlich sein. Abhängig von den Werten in ... führt die Länge von {...} möglicherweise nicht zur "richtigen" Antwort. In Lua 5.2+ wurde table.pack() zur table.pack() dieses Mangels eingeführt (im Beispiel gibt es eine Funktion, die diese Funktion in reiner Lua implementiert).

Idiomatische Verwendung

Da Varargs ihre Länge mitreißen, verwenden sie sie als Sequenzen, um das Problem mit Löchern in Tabellen zu vermeiden. Dies war nicht ihre beabsichtigte Verwendung und eine, für die die Referenzimplementierung von Lua nicht optimiert wird. Obwohl eine solche Verwendung in den Beispielen untersucht wird, wird sie im Allgemeinen missbilligt.

Grundlagen

Variadische Funktionen werden mit der Ellipsen-Syntax ... in der Argumentliste der Funktionsdefinition erstellt.

function id(...)
    return
end

Wenn Sie diese Funktion als id(1, 2, 3, 4, 5) aufgerufen haben, enthält ... (AKA die Vararg-Liste) die Werte 1, 2, 3, 4, 5 .

Funktionen können erforderliche Argumente sowie ... annehmen.

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

Die einfachste Möglichkeit, Elemente aus der Vararg-Liste zu ziehen, besteht darin, Variablen einfach zuzuweisen.

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

select() kann auch verwendet werden, um die Anzahl der Elemente zu ermitteln und Elemente indirekt aus ... extrahieren.

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

... kann zur Benutzerfreundlichkeit in eine Tabelle gepackt werden, indem Sie {...} . Dadurch werden alle Argumente in den sequentiellen Teil der Tabelle eingefügt.

5.2

table.pack(...) kann die vararg-Liste auch in eine Tabelle gepackt werden. Der Vorteil von table.pack(...) besteht darin, dass das Feld n der zurückgegebenen Tabelle auf den Wert von select('#', ...) . Dies ist wichtig, wenn Ihre Argumentliste nils enthalten kann (siehe Abschnitt "Anmerkungen" unten).

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

Die Vararg-Liste kann auch von Funktionen zurückgegeben werden. Das Ergebnis sind mehrere Rückgaben.

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

Fortgeschrittene Verwendung

Wie in den Basisbeispielen angegeben, können Sie variable Argumente und die Variablenargumentliste ( ... ) haben. Sie können diese Tatsache verwenden, um eine Liste rekursiv wie in anderen Sprachen (wie Haskell) auseinander zu ziehen. Nachfolgend finden Sie eine Implementierung von foldr() , die dies nutzt. Jeder rekursive Aufruf bindet den Kopf der vararg-Liste an x und leitet den Rest der Liste an einen rekursiven Aufruf weiter. Dies zerstört die Liste, bis es nur ein Argument gibt ( select('#', ...) == 0 ). Danach wird jeder Wert mit dem zuvor berechneten Ergebnis auf das Funktionsargument f angewendet.

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    

Weitere Funktionsdefinitionen, die diesen Programmierstil nutzen, finden Sie hier in Ausgabe # 3 bis Ausgabe # 8.

Luas einzige idiomatische Datenstruktur ist die Tabelle. Die Tabellenlänge Operator ist nicht definiert , wenn es nil s irgendwo befindet sich in einer Sequenz. Im Gegensatz zu Tabellen, respektiert die Vararg Liste explizit nil s wie in den grundlegenden Beispielen und die Erläuterungen Abschnitt angegeben (bitte diesen Abschnitt lesen , wenn Sie noch nicht haben). Mit wenig Aufwand kann die Vararg-Liste neben der Mutation jede Operation einer Tabelle ausführen. Dies macht die Vararg-Liste zu einem guten Kandidaten für die Implementierung unveränderlicher Tupel.

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

Der Hauptunterschied zwischen dem oben genannten und den Tabellen besteht darin, dass Tabellen veränderlich sind und Zeiger-Semantik aufweisen, wobei das Tupel diese Eigenschaften nicht hat. Außerdem können Tupel explizite nil und eine Operation mit nie definierter Länge haben.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow