1 2 3 4 5 6 7 8 | t = {} t[1] = "W" t[2] = "T" print(#t) -- Retourne 2 t[10] = "F" print(#t) -- Retourne 2, ça ne compte que les index consécutifs à partir de 1 t[4] = "?" print(#t) -- Retourne 4 ?! |
Lua
Mais, mais, président, ne vous rendez vous pas compte que c'est normal ?
# est un raccourci pour table.getn
, aka luaH_getn
.
La plus part des fonctions Lua de la lib standard qui prennent en argument une table supposent des clefs [1..n], ce qui
explique pourquoi t={} t[0]=0 print(#t)
affiche 0.
Il est (facilement) possible de les réecrires pour supposer des clefs [0..n] ou même, pour les gens moins sain
d'esprit, de prendre en compte seulement les nombres premiers.
Pour le comportement avec #t=4, il faut savoir comment la taille est calculée :
Try to find a boundary in table `t'. A `boundary' is an integer index
such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
Ensuite, la raison pour laquelle 4 a été choisi et pas 2 ou 10 dans ton exemple est que l'indice i ayant cette
propriété (i.e. la taille de la table) est cherché par dichotomie…
Si tu veux, tu peux réécrire luaH_getn pour qu'il fonctionne sur les tables non-standard, mais il sera moins efficace
du coup puisque tu devras calculer la taille en O(n).
Le Lua saybien.
Et pourquoi la taille ne serait-elle pas stockée avec la table ? On passe de O(log n) à O(1), pour un coût mémoire de 4 octets (allez, 8 octets pour les fous qui ont une table de plus de 4G, mais on peut supposer qu'un build normal de lua sera de 4 octets). Et ce coût est négligeable par rapport à la taille mémoire de la table. (Enfin, sauf pour des usages très particuliers.)
Enfin, ça semblerait logique. Non ?
La seule taille conservée est la taille de la table de hachage.
Il est possible de réécrire quelques fonctions pour conserver la taille et avoir du O(1), mais la gestion de la taille
est vraiment horrible et je ne pense pas que ça donnera quelque chose de plus "logique" que le #t=4 pointé par monsieur
le président de l'association Prologin puisque table.getn est clairement fait pour être utilisé avec des tables au
layout standard et il est impossible d'écrire un supertable.getn qui prendra en compte tous les éléments, surtout à
cause de :
la metamethod __index : ce n'est pas parce que #t renvoie 42 qu'il n'y aura que 42 valeurs pour les quelles t[x] n'est pas nil.
les weak tables la taille de ta table pourra bouger comme par magie parfois (e.g. au milieu d'une boucle qui ne modifie même pas les valeurs de cette table à cause du GC).
En gros, le comportement de table.getn est le plus simple. Il est prévue pour des tables avec des layouts standards. Il est possible d'écrire une fonction qui retournera une valeur correspondant plus ou moins au nombre d'élément, mais alors il faudra définir ce qu'est « le nombre d'élément »…