Comment obtenir le nombre d'entrées dans une table Lua?

132

Cela ressemble à une question "laissez-moi google pour vous", mais je ne trouve pas de réponse. L' #opérateur Lua ne compte que les entrées avec des clés entières, de même que table.getn:

tbl = {}
tbl["test"] = 47
tbl[1] = 48
print(#tbl, table.getn(tbl))   -- prints "1     1"

count = 0
for _ in pairs(tbl) do count = count + 1 end
print(count)            -- prints "2"

Comment obtenir le nombre de toutes les entrées sans les compter?

Roman Starkov
la source
3
@lhf: J'ai écrit un sérialiseur qui se souvient de chaque objet qu'il a vu, et la prochaine fois qu'il le voit, il émet une référence entière au lieu de l'objet. La manière naturelle d'écrire ceci est quelque chose comme dictionary[value] = #dictionary + 1, où #représente le nombre de tous les objets. Ce que je me demande, c'est pourquoi vous ne voulez pas ceci: dans tous les cas d'utilisation sains de # (voir la réponse de kaizer.se), le nombre de tous les objets est exactement égal à ce que # renvoie déjà; il semble que faire # tout compter est strictement une amélioration. Bien sûr, je suis un débutant Lua et je manque peut-être le point.
Roman Starkov
32
@lhf: Ce n'est pas gentil de votre part de remettre en question la compétence du programmeur en lui demandant pourquoi il doit faire quelque chose pour lequel tous les langages de programmation raisonnables ont une fonction simple.
Timwi
5
@Timwi: Ce n'est pas gentil de votre part de dire à l'un des auteurs du langage Lua que Lua ne fait pas partie des langages de programmation "raisonnables". ;-) BTW, je n'ai jamais eu besoin de cette information aussi.
Alexander Gladysh
5
Je ne pense pas avoir jamais utilisé toutes les fonctionnalités d'une seule langue. Cela ne veut pas dire qu'ils ne sont pas utiles aux autres :)
Roman Starkov
7
@sylvanaar À mon avis, l' #opérateur est simplement mal défini. C'est si facile à corriger: premièrement, rendez #déterministe, et deuxièmement, introduisez un nouvel opérateur ou une nouvelle fonction pour obtenir le sacré nombre. Fin de l'histoire ... Pourquoi doivent-ils être si têtus? :)
Roman Starkov

Réponses:

129

Vous avez déjà la solution dans la question - le seul moyen est d'itérer toute la table avec pairs(..).

function tablelength(T)
  local count = 0
  for _ in pairs(T) do count = count + 1 end
  return count
end

Notez également que la définition de l'opérateur "#" est un peu plus compliquée que cela. Permettez-moi d'illustrer cela en prenant ce tableau:

t = {1,2,3}
t[5] = 1
t[9] = 1

Selon le manuel, l' un des 3, 5 et 9 sont des résultats valides pour #t. La seule façon sensée de l'utiliser est avec des tableaux d'une partie contiguë sans valeurs nulles.

u0b34a0f6ae
la source
42
Je frémis encore au souvenir de mon expérience avec Lua, quand j'ai réalisé pour la première fois que la valeur de retour d'un opérateur de base comme #n'est pas déterministe.
Roman Starkov
5
Oh, c'est probablement déterministe. C'est exactement la même chose que lorsque le standard C laisse un comportement défini par l'implémentation. La raison pour laquelle il en est ainsi est que différents implémenteurs peuvent choisir différents choix d'implémentation.
Nakedible
19
According to the manual, any of 3, 5 and 9 are valid results for #t. Selon le manuel, appeler # sur des non-séquences n'est pas défini . Cela signifie que tout résultat (-1, 3, 3.14, 5, 9) est valide.
cubuspl42
6
Concernant les résultats valides: u0b34a0f6ae est correct pour Lua 5.1, tandis que cubuspl42 est correct pour Lua 5.2. Dans les deux cas, le tout est complètement fou.
Jeremy
9
Le fait que # sur une non-séquence ne génère pas d'exception n'est qu'une des raisons pour lesquelles utiliser lua un peu comme se couper pour se sentir mieux.
boatcoder
21

Vous pouvez configurer une méta-table pour suivre le nombre d'entrées, cela peut être plus rapide qu'une itération si ces informations sont fréquemment nécessaires.

ergosys
la source
Existe-t-il un moyen pratique de gérer l'effacement des entrées avec cette méthode?
u0b34a0f6ae
Malheureusement, il semble que la fonction __newindex ne se déclenche pas sur les affectations nulles à moins que l'index n'existe pas, donc il semble que vous deviez canaliser la suppression des entrées via une fonction spéciale.
ergosys
1
Vous devez stocker les données dans une table séparée (par exemple accessible en tant que valeur positive pour __index et __newindex). Ensuite, __index et __newindex se déclencheraient pour chaque accès à la table. Vous devriez vérifier si les performances sont acceptables.
Alexander Gladysh
@Alexander: Ah oui, et puis le prochain point d'achoppement: si vous proxy la table, alors l'itération normale par paires ne fonctionne pas. Cela sera possible de résoudre dans Lua 5.2, j'ai entendu.
u0b34a0f6ae
Il y aurait des métaméthodes __pairs et __ipairs dans la version 5.2 ... Si vous voulez le faire dans la version 5.1, vous devrez remplacer la fonction paires () par la vôtre. Mais c'est probablement trop. :-)
Alexander Gladysh
3

Il y a un moyen, mais cela peut être décevant: utilisez une variable supplémentaire (ou l'un des champs de la table) pour stocker le nombre, et augmentez-le à chaque fois que vous faites une insertion.

count = 0
tbl = {}

tbl["test"] = 47
count = count + 1

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Il n'y a pas d'autre moyen, l'opérateur # ne fonctionnera que sur des tables de type tableau avec des clés consécutives.

kikito
la source
3
Cela peut être automatisé avec une table proxy et des métaméthodes, comme mentionné par la réponse d'
ergosys
J'ai eu l'impression d'après les commentaires que le truc de la table proxy / métaméthodes ne supporte pas encore totalement ce scénario, donc j'accepterai cela comme le meilleur moyen actuellement disponible.
Roman Starkov
Le comptage est le seul moyen pour les tables, et l'ajout de lignes lors de la création des tables est préférable à une fonction pour les compter à chaque fois que vous en avez besoin. Vous pouvez ajouter une clé à la fin avec la valeur définie sur le nombre.
Henrik Erlandsson
2

Le moyen le plus simple que je connaisse pour obtenir le nombre d'entrées dans une table est d'utiliser «#». #tableName obtient le nombre d'entrées tant qu'elles sont numérotées:

tbl={
    [1]
    [2]
    [3]
    [4]
    [5]
}
print(#tbl)--prints the highest number in the table: 5

Malheureusement, s'ils ne sont pas numérotés, cela ne fonctionnera pas.

Surge12
la source
2

Vous pouvez utiliser la bibliothèque penlight . Cela a une fonction sizequi donne la taille réelle de la table.

Il a implémenté de nombreuses fonctions dont nous pourrions avoir besoin lors de la programmation et manquantes dans Lua.

Voici l'exemple pour l'utiliser.

> tablex = require "pl.tablex"
> a = {}
> a[2] = 2
> a[3] = 3 
> a['blah'] = 24

> #a
0

> tablex.size(a)
3

la source
1
local function CountedTable(x)
    assert(type(x) == 'table', 'bad parameter #1: must be table')

    local new_t = {}
    local mt = {}

    -- `all` will represent the number of both
    local all = 0
    for k, v in pairs(x) do
        all = all + 1
    end

    mt.__newindex = function(t, k, v)
        if v == nil then
            if rawget(x, k) ~= nil then
                all = all - 1
            end
        else
            if rawget(x, k) == nil then
                all = all + 1
            end
        end

        rawset(x, k, v)
    end

    mt.__index = function(t, k)
        if k == 'totalCount' then return all
        else return rawget(x, k) end
    end

    return setmetatable(new_t, mt)
end

local bar = CountedTable { x = 23, y = 43, z = 334, [true] = true }

assert(bar.totalCount == 4)
assert(bar.x == 23)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = nil
assert(bar.totalCount == 3)
bar.x = 24
bar.x = 25
assert(bar.x == 25)
assert(bar.totalCount == 4)
Sleepwom
la source
1
Lors de la publication d'une réponse, il est recommandé de publier la quantité minimale de code qui répond directement à une question et d'expliquer comment le code répond à la question. Regardez ici .
cst1992
__newindexn'appelle que lorsqu'une nouvelle clé est définie, il n'y a donc aucune chance d'appeler __newindexlorsque nous définissons nilsur une clé existe.
Frank AK
-1

apparaît lorsque les éléments de la table sont ajoutés par la méthode insert, getn retournera correctement. Sinon, il faut compter tous les éléments

mytable = {}
element1 = {version = 1.1}
element2 = {version = 1.2}
table.insert(mytable, element1)
table.insert(mytable, element2)
print(table.getn(mytable))

Il imprimera 2 correctement

Yongxin Zhang
la source