Lua
Variadische Argumente
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.
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.