Recherche…


Introduction

Les varargs , comme on les appelle communément, permettent aux fonctions de prendre un nombre arbitraire d'arguments sans spécification. Tous les arguments donnés à une telle fonction sont regroupés dans une structure unique appelée liste vararg ; qui est écrit comme ... dans Lua. Il existe des méthodes de base pour extraire le nombre d'arguments donnés et la valeur de ces arguments à l'aide de la fonction select() , mais des modèles d'utilisation plus avancés peuvent tirer parti de la structure pour son utilitaire complet.

Syntaxe

  • ... - Fait la fonction dont la liste des arguments dans laquelle cela apparaît une fonction variadique
  • select (what, ...) - Si 'what' est un nombre compris entre 1 et le nombre d'éléments du vararg, renvoie l'élément 'what'th au dernier élément de vararg. Le retour sera nul si l'index est hors limites. Si 'what' est la chaîne '#', retourne le nombre d'éléments dans vararg.

Remarques

Efficacité

La liste vararg est implémentée comme une liste liée dans l'implémentation PUC-Rio du langage, cela signifie que les index sont O (n). Cela signifie que l'itération sur les éléments d'un vararg en utilisant select() , comme l'exemple ci-dessous, est une opération O (n ^ 2).

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

Si vous prévoyez d'itérer sur les éléments d'une liste vararg, commencez par empaqueter la liste dans une table. Les accès à la table sont O (1), donc l'itération est O (n) au total. Ou, si vous le souhaitez, consultez l'exemple foldr() de la section d'utilisation avancée; il utilise la récursivité pour parcourir une liste vararg dans O (n).

Définition de la longueur de séquence

Le vararg est utile dans la mesure où la longueur du vararg respecte tous les nils explicitement passés (ou calculés). Par exemple.

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

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

Ce comportement est en conflit avec le comportement des tables, où l'opérateur de longueur # ne fonctionne pas avec des «trous» (nils intégrés) dans les séquences. Le calcul de la longueur d'une table avec des trous n'est pas défini et on ne peut pas s'y fier. Donc, en fonction des valeurs de ... , prendre la longueur de {...} peut ne pas donner la réponse "correcte" . Dans Lua 5.2+, table.pack() été introduit pour gérer cette lacune (il y a une fonction dans l'exemple qui implémente cette fonction dans Lua pur).

Utilisation idiomatique

Parce que les varargs portent leur longueur, les gens les utilisent comme séquences pour éviter le problème avec des trous dans les tableaux. Ce n'était pas leur usage prévu et l'implémentation de référence de Lua n'optimise pas. Bien que cet usage soit exploré dans les exemples, il est généralement mal vu.

Les bases

Les fonctions variables sont créées à l'aide de la syntaxe ... ellipses dans la liste d'arguments de la définition de fonction.

function id(...)
    return
end

Si vous appeliez cette fonction comme id(1, 2, 3, 4, 5) alors ... (AKA la liste vararg) contiendrait les valeurs 1, 2, 3, 4, 5 .

Les fonctions peuvent prendre les arguments requis ainsi que ...

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

La méthode la plus simple pour extraire des éléments de la liste vararg consiste simplement à en attribuer des variables.

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

select() peut également être utilisé pour trouver le nombre d'éléments et extraire des éléments de ... indirectement.

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

... peut être emballé dans un tableau pour faciliter son utilisation, en utilisant {...} . Cela place tous les arguments dans la partie séquentielle de la table.

5.2

table.pack(...) peut également être utilisé pour empaqueter la liste vararg dans une table. L'avantage de table.pack(...) est qu'il définit le champ n de la table renvoyée sur la valeur de select('#', ...) . Ceci est important si votre liste d'arguments peut contenir des nils (voir la section des remarques ci-dessous).

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

La liste vararg peut également être renvoyée par les fonctions. Le résultat est plusieurs retours.

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

Utilisation avancée

Comme indiqué dans les exemples de base, vous pouvez avoir des arguments liés aux variables et la liste des arguments variables ( ... ). Vous pouvez utiliser ce fait pour séparer récursivement une liste comme vous le feriez dans d'autres langues (comme Haskell). Vous trouverez ci-dessous une implémentation de foldr() qui en tire parti. Chaque appel récursif lie la tête de la liste vararg à x et transmet le reste de la liste à un appel récursif. Cela déstructure la liste jusqu'à ce qu'il n'y ait qu'un seul argument ( select('#', ...) == 0 ). Après cela, chaque valeur est appliquée à l'argument de fonction f avec le résultat calculé précédemment.

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    

Vous pouvez trouver d' autres définitions de fonctions qui tirent parti de ce style de programmation ici dans le numéro 3 à numéro 8.

La seule structure de données idiomatique de Lua est la table. L'opérateur de longueur de la table n'est pas défini s'il y a des nil s situés n'importe où dans une séquence. Contrairement à des tables, la liste vararg respecte explicitement nil s comme indiqué dans les exemples de base et la section des remarques (s'il vous plaît lire cet article si vous n'avez pas encore). Avec peu de travail, la liste vararg peut effectuer toutes les opérations qu'une table peut avoir en plus de la mutation. Cela fait de la liste vararg un bon candidat pour l'implémentation de tuples immuables.

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 différence entre ce qui précède et les tableaux est que les tables sont mutables et ont une sémantique de pointeur, où le tuple ne possède pas ces propriétés. De plus, les n-uplets peuvent contenir des nil explicites et une opération de longueur jamais définie.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow