Buscar..


Introducción

Los Varargs , como se conocen comúnmente, permiten que las funciones tomen un número arbitrario de argumentos sin especificación. Todos los argumentos dados a una función de este tipo se empaquetan en una estructura única conocida como la lista vararg ; que se escribe como ... en lua. Existen métodos básicos para extraer el número de argumentos dados y el valor de esos argumentos utilizando la función select() , pero los patrones de uso más avanzados pueden aprovechar la estructura para su utilidad completa.

Sintaxis

  • ... - Hace que la función cuyos argumentos en la lista en la que aparece aparezca sea una función variable.
  • seleccione (qué, ...) - Si 'qué' es un número en el rango 1 al número de elementos en el vararg, devuelve el elemento 'qué' al último elemento en el vararg. La devolución será nula si el índice está fuera de límites. Si 'qué' es la cadena '#', devuelve el número de elementos en el vararg.

Observaciones

Eficiencia

La lista vararg se implementa como una lista vinculada en la implementación del lenguaje de la PUC-Rio, esto significa que los índices son O (n). Eso significa que iterar sobre los elementos en un vararg usando select() , como en el siguiente ejemplo, es una operación O (n ^ 2).

for i = 1, select('#', ...) do
    print(select(i, ...))
end

Si planea iterar sobre los elementos en una lista vararg, primero guarde la lista en una tabla. Los accesos de tabla son O (1), por lo que la iteración es O (n) en total. O, si lo desea, vea el ejemplo de foldr() en la sección de uso avanzado; utiliza la recursividad para iterar sobre una lista vararg en O (n).

Definición de longitud de secuencia

El vararg es útil porque la longitud del vararg respeta cualquier nils explícitamente pasados ​​(o computados). Por ejemplo.

function test(...)
    return select('#', ...)
end

test()             --> 0
test(nil, 1, nil)  --> 3

Sin embargo, este comportamiento entra en conflicto con el comportamiento de las tablas, donde el operador de longitud # no funciona con 'agujeros' (nils incrustados) en secuencias. El cálculo de la longitud de una mesa con agujeros no está definido y no se puede confiar en él. Entonces, dependiendo de los valores en ... , tomar la longitud de {...} puede no resultar en la respuesta 'correcta' . En Lua 5.2+ se introdujo table.pack() para manejar esta deficiencia (hay una función en el ejemplo que implementa esta función en Lua puro).

Uso idiomático

Debido a que los varargs llevan su longitud, las personas los usan como secuencias para evitar el problema con los agujeros en las tablas. Este no fue su uso previsto y la implementación de referencia de Lua no se optimiza. Aunque tal uso se explora en los ejemplos, generalmente está mal visto.

Lo esencial

Las funciones Variadic se crean usando la sintaxis de ... puntos suspensivos en la lista de argumentos de la definición de función.

function id(...)
    return
end

Si llama a esta función como id(1, 2, 3, 4, 5) entonces ... (También conocida como la lista vararg) contendría los valores 1, 2, 3, 4, 5 .

Las funciones pueden tomar argumentos requeridos, así como ...

function head(x, ...)
    return x
end

La forma más fácil de extraer elementos de la lista vararg es simplemente asignar variables de ella.

function head3(...)
    local a, b, c = ...
    return a, b, c
end

select() también se puede usar para encontrar el número de elementos y extraer elementos de ... indirectamente.

function my_print(...)
    for i = 1, select('#', ...) do
        io.write(tostring(select(i, ...)) .. '\t')
    end
    io.write '\n'
end

... se puede empaquetar en una tabla para facilitar su uso, utilizando {...} . Esto coloca todos los argumentos en la parte secuencial de la tabla.

5.2

table.pack(...) también se puede usar para empaquetar la lista vararg en una tabla. La ventaja de table.pack(...) es que establece el campo n de la tabla devuelta en el valor de select('#', ...) . Esto es importante si su lista de argumentos puede contener nils (consulte la sección de comentarios a continuación).

function my_tablepack(...)
    local t = {...}
    t.n = select('#', ...)
    return t
end

La lista vararg también puede devolverse desde las funciones. El resultado es múltiples devoluciones.

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 avanzado

Como se indica en los ejemplos básicos, puede tener argumentos de límite variable y la lista de argumentos variable ( ... ). Puede usar este hecho para separar una lista recursivamente como lo haría en otros idiomas (como Haskell). A continuación se muestra una implementación de foldr() que se aprovecha de eso. Cada llamada recursiva enlaza el encabezado de la lista vararg a x , y pasa el resto de la lista a una llamada recursiva. Esto destruye la lista hasta que solo hay un argumento ( select('#', ...) == 0 ). Después de eso, cada valor se aplica al argumento de función f con el resultado calculado previamente.

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    

Puede encontrar otras definiciones de funciones que aprovechan este estilo de programación aquí en el Número 3 a través del Número 8.

La única estructura de datos idiomática de Lua es la tabla. El operador de longitud de la tabla no está definido si no hay nil ubicados en ninguna parte de una secuencia. A diferencia de las tablas, la lista vararg respeta explícita nil s como se indica en los ejemplos básicos y la sección de observaciones (por favor leer esa sección, si no lo ha hecho todavía). Con poco trabajo, la lista de vararg puede realizar todas las operaciones que puede realizar una tabla además de la mutación. Esto hace que la lista vararg sea un buen candidato para implementar tuplas inmutables.

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 principal diferencia entre lo que está arriba y las tablas es que las tablas son mutables y tienen una semántica de puntero, donde la tupla no tiene esas propiedades. Además, las tuplas pueden contener nil s explícitos y tener una operación de longitud nunca definida.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow