Lua
Argomenti Variadici
Ricerca…
introduzione
I vararg , come sono comunemente noti, consentono alle funzioni di assumere un numero arbitrario di argomenti senza specifica. Tutti gli argomenti dati a tale funzione sono raggruppati in un'unica struttura nota come lista vararg ; che è scritto come ...
in Lua. Esistono metodi di base per estrarre il numero di argomenti dati e il valore di tali argomenti usando la funzione select()
, ma schemi di utilizzo più avanzati possono sfruttare la struttura per la sua piena utilità.
Sintassi
- ... - Crea la funzione i cui argomenti elencano in cui appare una funzione variadica
- select (what, ...) - Se 'what' è un numero compreso nell'intervallo 1 al numero di elementi nel vararg, restituisce 'what'th all'ultimo elemento nel vararg. Il ritorno sarà nullo se l'indice è fuori limite. Se 'what' è la stringa '#', restituisce il numero di elementi nel vararg.
Osservazioni
Efficienza
L'elenco vararg è implementato come elenco collegato nell'implementazione PUC-Rio della lingua, questo significa che gli indici sono O (n). Ciò significa che l'iterazione degli elementi in un vararg usando select()
, come nell'esempio seguente, è un'operazione O (n ^ 2).
for i = 1, select('#', ...) do
print(select(i, ...))
end
Se si pianifica di iterare sugli elementi in una lista vararg, prima imballare l'elenco in una tabella. Gli accessi alle tabelle sono O (1), quindi l'iterazione è O (n) in totale. Oppure, se sei così inclinato, vedi l'esempio di foldr()
dalla sezione di utilizzo avanzato; usa ricorsione per scorrere su una lista di vararg in O (n).
Definizione della lunghezza della sequenza
Il vararg è utile in quanto la lunghezza del vararg rispetta qualsiasi nils esplicitamente passato (o calcolato). Per esempio.
function test(...)
return select('#', ...)
end
test() --> 0
test(nil, 1, nil) --> 3
Questo comportamento è in conflitto con il comportamento delle tabelle, tuttavia, in cui l'operatore #
lunghezza non funziona con "buchi" (nils incorporati) in sequenze. Calcolare la lunghezza di una tabella con buchi non è definito e non può essere considerato affidabile. Quindi, a seconda dei valori in ...
, prendere la lunghezza di {...}
potrebbe non dare la risposta 'corretta' . In Lua 5.2+ è stato introdotto table.pack()
per gestire questa deficienza (esiste una funzione nell'esempio che implementa questa funzione in puro Lua).
Uso idiomatico
Poiché le varici trasportano la loro lunghezza, le persone le usano come sequenze per evitare il problema con i buchi nelle tabelle. Questo non era il loro uso previsto e l'implementazione di riferimento di Lua non è ottimizzata per. Sebbene tale uso sia esplorato negli esempi, generalmente è disapprovato.
Nozioni di base
Le funzioni Variadic vengono create utilizzando la sintassi ...
ellissi nella lista degli argomenti della definizione della funzione.
function id(...)
return
end
Se hai chiamato questa funzione come id(1, 2, 3, 4, 5)
allora ...
(AKA la lista vararg) conterrebbe i valori 1, 2, 3, 4, 5
.
Le funzioni possono accettare argomenti richiesti e ...
function head(x, ...)
return x
end
Il modo più semplice per estrarre elementi dalla lista vararg è semplicemente assegnargli delle variabili.
function head3(...)
local a, b, c = ...
return a, b, c
end
select()
può anche essere usato per trovare il numero di elementi ed estrarre elementi da ...
indirettamente.
function my_print(...)
for i = 1, select('#', ...) do
io.write(tostring(select(i, ...)) .. '\t')
end
io.write '\n'
end
...
può essere inserito in una tabella per facilità d'uso, usando {...}
. Questo pone tutti gli argomenti nella parte sequenziale della tabella.
table.pack(...)
può anche essere utilizzato per impacchettare la lista vararg in una tabella. Il vantaggio di table.pack(...)
è che imposta il campo n
della tabella restituita sul valore di select('#', ...)
. Questo è importante se il tuo elenco di argomenti può contenere nils (vedi la sezione commenti sotto).
function my_tablepack(...)
local t = {...}
t.n = select('#', ...)
return t
end
La lista vararg può anche essere restituita da funzioni. Il risultato è più ritorni.
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
Uso avanzato
Come affermato negli esempi di base, è possibile avere argomenti con limiti variabili e l'elenco di argomenti variabili ( ...
). Puoi usare questo fatto per separare ricorsivamente una lista come faresti in altre lingue (come Haskell). Di seguito è riportata un'implementazione di foldr()
che sfrutta questo. Ogni chiamata ricorsiva lega la testa dell'elenco vararg a x
e passa il resto dell'elenco a una chiamata ricorsiva. Questo distrugge l'elenco finché non c'è un solo argomento ( select('#', ...) == 0
). Successivamente, ciascun valore viene applicato all'argomento della funzione f
con il risultato precedentemente calcolato.
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
Puoi trovare altre definizioni di funzioni che sfruttano questo stile di programmazione qui nel numero 3 attraverso il numero 8.
L'unica struttura dati idiomatica di Lua è la tabella. L'operatore della lunghezza della tabella non è definito se vi sono nil
in una sequenza. A differenza delle tabelle, la lista vararg rispetta esplicitamente nil
s come indicato negli esempi di base e nella sezione dei commenti (si prega di leggere quella sezione se non l'hai ancora). Con poco lavoro la lista vararg può eseguire ogni operazione che una tabella può oltre alla mutazione. Ciò rende la lista dei vararg un buon candidato per l'implementazione di tuple immutabili.
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
La principale differenza tra ciò che è sopra e le tabelle è che le tabelle sono mutabili e hanno la semantica del puntatore, dove la tupla non ha quelle proprietà. Inoltre, le tuple possono contenere esplicitamente nil
e hanno un'operazione di lunghezza mai definita.