Lua
Variadiska argument
Sök…
Introduktion
Varargs , som de är vanligt kända, tillåter funktioner att ta ett godtyckligt antal argument utan specifikation. Alla argument som ges till en sådan funktion förpackas i en enda struktur som kallas vararg-listan ; som är skrivet som ...
i Lua. Det finns grundläggande metoder för att extrahera antalet givna argument och värdet på dessa argument med funktionen select()
, men mer avancerade användningsmönster kan utnyttja strukturen till dess fulla verktyg.
Syntax
- ... - Gör den funktion vars argumentlista där detta visas en variadisk funktion
- select (what, ...) - Om 'what' är ett nummer i intervall 1 till antalet element i vararg, returnerar 'what'th elementet till det sista elementet i vararg. Avkastningen blir noll om indexet är utanför gränserna. Om 'vad' är strängen '#' returnerar du antalet element i vararg.
Anmärkningar
Effektivitet
Vararg-listan implementeras som en länkad lista i PUC-Rio-implementeringen av språket, det betyder att index är O (n). Det betyder att iterering över elementen i en vararg med select()
, som exemplet nedan, är en O (n ^ 2) operation.
for i = 1, select('#', ...) do
print(select(i, ...))
end
Om du planerar att iterera över elementen i en vararg-lista, packa först listan i en tabell. Tabellåtkomstar är O (1), så iterering är O (n) totalt. Eller, om du är så benägen, se foldr()
exemplet från avsnittet avancerad användning; den använder rekursion för att iterera över en vararg-lista i O (n).
Sekvens Längd Definition
Vararg är användbar i och med att längden på vararg respekterar alla uttryckligen passerade (eller beräknade) nils. Till exempel.
function test(...)
return select('#', ...)
end
test() --> 0
test(nil, 1, nil) --> 3
Detta beteende strider emellertid med beteendet hos tabeller, där längdoperatören #
inte fungerar med 'hål' (inbäddade nils) i sekvenser. Beräkningen av bordets längd med hål är inte definierad och kan inte lita på. Så beroende på värdena i ...
kanske längden på {...}
inte resulterar i det "korrekta" svaret. I Lua 5.2+ infördes table.pack()
för att hantera denna brist (det finns en funktion i exemplet som implementerar denna funktion i ren Lua).
Idiomatisk användning
Eftersom varargs bär runt sin längd använder människor dem som sekvenser för att undvika problemet med hål i bord. Detta var inte deras avsedda användning och en som Luas referensimplementering inte optimerar för. Även om en sådan användning undersöks i exemplen, är den i allmänhet rynkad på.
Grunderna
Variadfunktioner skapas med ...
ellipsesyntaxen i argumentlistan för funktionsdefinitionen.
function id(...)
return
end
Om du kallade den här funktionen som id(1, 2, 3, 4, 5)
skulle ...
(AKA vararg-listan) innehålla värdena 1, 2, 3, 4, 5
.
Funktioner kan ta nödvändiga argument såväl som ...
function head(x, ...)
return x
end
Det enklaste sättet att dra element från vararg-listan är att helt enkelt tilldela variabler från den.
function head3(...)
local a, b, c = ...
return a, b, c
end
select()
kan också användas för att hitta antalet element och extrahera element från ...
indirekt.
function my_print(...)
for i = 1, select('#', ...) do
io.write(tostring(select(i, ...)) .. '\t')
end
io.write '\n'
end
...
kan förpackas i ett bord för att underlätta användningen genom att använda {...}
. Detta placerar alla argument i den sekvensiella delen av tabellen.
table.pack(...)
kan också användas för att packa vararg-listan i en tabell. Fördelen med table.pack(...)
är att det sätter n
fältet i den returnerade tabellen till värdet på select('#', ...)
. Detta är viktigt om din argumentlista kan innehålla nils (se avsnittet om kommentarer nedan).
function my_tablepack(...)
local t = {...}
t.n = select('#', ...)
return t
end
Vararg-listan kan också returneras från funktioner. Resultatet är flera returer.
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
Avancerad användning
Som anges i de grundläggande exemplen kan du ha variabelt bundna argument och variabelargumentlistan ( ...
). Du kan använda detta faktum för att rekursivt dra isär en lista som du skulle göra på andra språk (som Haskell). Nedan foldr()
en implementering av foldr()
som utnyttjar det. Varje rekursivt samtal binder huvudet på vararg-listan till x
och överför resten av listan till ett rekursivt samtal. Detta förstör listan tills det bara finns ett argument ( select('#', ...) == 0
). Därefter appliceras varje värde på funktionsargumentet f
med det tidigare beräknade resultatet.
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
Du kan hitta andra funktionsdefinitioner som utnyttjar denna programmeringsstil här i nummer 3 till nummer 8.
Luas enda idiomatiska datastruktur är tabellen. Tabelllängdoperatören är odefinierad om det finns nil
är placerade var som helst i en sekvens. Till skillnad från tabeller, respekterar vararg-listan explicita nil
som anges i de grundläggande exemplen och kommentarerna (läs det avsnittet om du inte har gjort det ännu). Med lite arbete kan vararg-listan utföra varje operation som en tabell kan förutom mutation. Detta gör vararg-listan till en bra kandidat för implementering av immutable tuples.
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
Den huvudsakliga skillnaden mellan vad som är ovan och tabeller är att tabeller är muterbara och har pekersemantik, där tupeln inte har dessa egenskaper. Dessutom kan tupplar innehålla tydliga nil
och ha en aldrig odefinierad längdoperation.