Lua
Variadische argumenten
Zoeken…
Invoering
Varargs , zoals ze algemeen bekend staan, staan functies toe om een willekeurig aantal argumenten zonder specificatie aan te nemen. Alle argumenten die aan een dergelijke functie worden gegeven, zijn verpakt in een enkele structuur die bekend staat als de vararglijst ; die is geschreven als ...
in Lua. Er zijn basismethoden voor het extraheren van het aantal gegeven argumenten en de waarde van die argumenten met de functie select()
, maar geavanceerdere gebruikspatronen kunnen de structuur optimaal benutten.
Syntaxis
- ... - Maakt de functie waarvan de lijst met argumenten waarin deze verschijnt een variadische functie
- select (what, ...) - Als 'what' een getal is in bereik 1 tot het aantal elementen in de vararg, retourneert het 'what'th-element naar het laatste element in de vararg. Het rendement is nul als de index buiten bereik is. Als 'wat' de tekenreeks '#' is, retourneert het aantal elementen in de vararg.
Opmerkingen
rendement
De vararg-lijst wordt geïmplementeerd als een gekoppelde lijst in de PUC-Rio-implementatie van de taal, dit betekent dat indexen O (n) zijn. Dat betekent dat het herhalen van de elementen in een vararg met behulp van select()
, zoals het onderstaande voorbeeld, een O (n ^ 2) -bewerking is.
for i = 1, select('#', ...) do
print(select(i, ...))
end
Als u van plan bent de elementen in een vararglijst te herhalen, pak dan eerst de lijst in een tabel in. Tabeltoegang is O (1), dus iteratie is O (n) in totaal. Of, als u daartoe geneigd bent, zie het voorbeeld van foldr()
in het gedeelte voor geavanceerd gebruik; het gebruikt recursie om over een vararglijst in O (n) te itereren.
Volgorde Lengte Definitie
De vararg is nuttig omdat de lengte van de vararg expliciet doorgegeven (of berekende) nils respecteert. Bijvoorbeeld.
function test(...)
return select('#', ...)
end
test() --> 0
test(nil, 1, nil) --> 3
Dit gedrag is echter in strijd met het gedrag van tabellen, waarbij de lengte-operator #
niet werkt met 'gaten' (ingesloten nils) in reeksen. Het berekenen van de lengte van een tafel met gaten is niet gedefinieerd en kan niet worden vertrouwd. Afhankelijk van de waarden in ...
kan het nemen van de lengte van {...}
dus niet leiden tot het 'juiste' antwoord. In Lua 5.2+ is table.pack()
geïntroduceerd om dit tekort aan te pakken (er is een functie in het voorbeeld die deze functie implementeert in pure Lua).
Idiomatisch gebruik
Omdat varargs hun lengte dragen, gebruiken mensen ze als sequenties om het probleem met gaten in tabellen te voorkomen. Dit was niet het beoogde gebruik en één waarvoor de referentie-implementatie van Lua niet optimaliseert. Hoewel een dergelijk gebruik in de voorbeelden wordt onderzocht, wordt het in het algemeen afgekeurd.
Basics
Variadische functies worden gemaakt met behulp van de ...
ellipsen syntaxis in de lijst met argumenten van de functiedefinitie.
function id(...)
return
end
Als u deze functie als id(1, 2, 3, 4, 5)
) zou noemen id(1, 2, 3, 4, 5)
dan ...
(AKA de vararglijst) zou de waarden 1, 2, 3, 4, 5
.
Voor functies kunnen vereiste argumenten nodig zijn, evenals ...
function head(x, ...)
return x
end
De eenvoudigste manier om elementen uit de vararglijst te halen, is om er eenvoudig variabelen aan toe te wijzen.
function head3(...)
local a, b, c = ...
return a, b, c
end
select()
kan ook worden gebruikt om het aantal elementen te vinden en elementen te extraheren van ...
indirect.
function my_print(...)
for i = 1, select('#', ...) do
io.write(tostring(select(i, ...)) .. '\t')
end
io.write '\n'
end
...
kan in een tafel worden verpakt voor gebruiksgemak, met behulp van {...}
. Hiermee worden alle argumenten in het volgende deel van de tabel geplaatst.
table.pack(...)
kan ook worden gebruikt om de vararglijst in een tabel in te pakken. Het voordeel van table.pack(...)
is dat het veld n
van de geretourneerde tabel wordt ingesteld op de waarde select('#', ...)
. Dit is belangrijk als uw lijst met argumenten nul kan bevatten (zie opmerkingen hieronder).
function my_tablepack(...)
local t = {...}
t.n = select('#', ...)
return t
end
De vararglijst kan ook worden geretourneerd vanuit functies. Het resultaat is meerdere retouren.
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
Geavanceerd gebruik
Zoals vermeld in de basisvoorbeelden, kunt u variabel gebonden argumenten en de lijst met variabele argumenten ( ...
) gebruiken. U kunt dit feit gebruiken om een lijst recursief uit elkaar te trekken, net zoals in andere talen (zoals Haskell). Hieronder is een implementatie van foldr()
die daarvan profiteert. Elke recursieve aanroep bindt de kop van de vararglijst aan x
en geeft de rest van de lijst door aan een recursieve aanroep. Hierdoor wordt de lijst vernietigd totdat er slechts één argument is ( select('#', ...) == 0
). Daarna wordt elke waarde toegepast op het functieargument f
met het eerder berekende resultaat.
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
U kunt andere functiedefinities die deze programmeerstijl gebruiken hier vinden in nummer 3 tot en met nummer 8.
De enige idiomatische gegevensstructuur van Lua is de tabel. De operator voor de lengte van de tabel is niet gedefinieerd als er zich nil
ergens in een reeks bevinden. In tegenstelling tot tafels, de vararg lijst opzichten expliciete nil
s zoals vermeld in de basis voorbeelden en de opmerkingen sectie (lees dat gedeelte als u nog niet). Met weinig werk kan de vararglijst elke bewerking uitvoeren die een tabel naast mutatie kan uitvoeren. Dit maakt de vararglijst een goede kandidaat voor het implementeren van onveranderlijke tupels.
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
Het belangrijkste verschil tussen wat hierboven en tabellen is, is dat tabellen veranderlijk zijn en pointer-semantiek hebben, waarbij de tuple die eigenschappen niet heeft. Bovendien kunnen tupels expliciete nil
en een nooit-ongedefinieerde lengte-bewerking hebben.