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.

5.2

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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow