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.

5,2

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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow