Как получить количество записей в таблице Lua?


132

Звучит как вопрос "позволь мне погуглить для тебя", но почему-то я не могу найти ответа. Оператор Lua #считает только записи с целочисленными ключами, и то же самое 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"

Как получить количество всех записей, не считая их?


3
@lhf: я написал сериализатор, который запоминает каждый объект, который он видел, и в следующий раз, когда он его увидит, он выдает целочисленную ссылку вместо объекта. Естественный способ записать это примерно так dictionary[value] = #dictionary + 1: где #обозначает количество всех объектов. Что я интересно , почему вы не хотите: во всех здравомыслящих прецедентах для # (см ответа на kaizer.se), подсчет всех объектов в точности равен тому , что уже # возвратов; похоже, что # подсчитывать все - это строго улучшение. Конечно, я новичок в Lua и могу упустить суть.
Роман Старков

32
@lhf: Это нехорошо с вашей стороны ставить под сомнение компетентность программиста, спрашивая, почему ему нужно делать то, для чего все разумные языки программирования имеют простую функцию.
Timwi

5
@Timwi: Это неприятно с вашей стороны сказать одному из авторов языка Lua, что Lua не входит в число «разумных» языков программирования. ;-) Кстати, эта информация мне тоже никогда не нужна.
Александр Гладыш

5
Не думаю, что когда-либо использовал все возможности одного языка. Это не значит, что они бесполезны для других :)
Роман Старков

7
@sylvanaar На мой взгляд, #оператор просто некорректно определен. Это так легко исправить: во-первых, сделайте #детерминированным, а во-вторых, введите новый оператор или функцию, чтобы получить счет. Конец истории ... Почему они должны быть такими упрямыми? :)
Роман Старков

Ответы:


129

У вас уже есть решение вопроса - единственный способ - перебрать всю таблицу с помощью pairs(..).

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

Также обратите внимание, что определение оператора "#" немного сложнее. Позвольте мне проиллюстрировать это, взяв эту таблицу:

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

Согласно руководству, любые из 3, 5 и 9 являются действительными результатами для #t. Единственный разумный способ его использования - использовать массивы из одной непрерывной части без значений nil.


42
Я до сих пор содрогаюсь при воспоминании о моем опыте работы с Lua, когда я впервые понял, что возвращаемое значение базового оператора вроде #не детерминировано.
Роман Старков

5
О, скорее всего, детерминировано. Это в точности то же самое, что и когда стандарт C оставляет что-то, определяемое реализацией. Причина этого в том, что разные разработчики могут выбирать разные варианты реализации.
Nakedible

19
According to the manual, any of 3, 5 and 9 are valid results for #t, Согласно руководству, вызов # для непоследовательностей не определен . Это означает, что любой результат (-1, 3, 3.14, 5, 9) действителен.
cubuspl42

6
Что касается допустимых результатов: u0b34a0f6ae правильно для Lua 5.1, а cubuspl42 - для Lua 5.2. В любом случае все это безумие.
Джереми

9
Тот факт, что # в непоследовательности не генерирует исключения, - это лишь одна из причин, по которой использование lua немного похоже на сокращение себя, чтобы почувствовать себя лучше.
boatcoder

21

Вы можете настроить мета-таблицу для отслеживания количества записей, это может быть быстрее, чем итерация, если эта информация требуется часто.


Есть ли удобный способ удалить записи с помощью этого метода?
u0b34a0f6ae

К сожалению, похоже, что функция __newindex не срабатывает при присваивании nil, если индекс не существует, поэтому кажется, что вам придется выполнять удаление записи через специальную функцию.
ergosys

1
Вы должны хранить данные в отдельной таблице (например, доступной как upvalue для __index и __newindex). Тогда и __index, и __newindex будут срабатывать для каждого доступа к таблице. Вы должны проверить, приемлемая ли производительность.
Александр Гладыш

@Alexander: Ах, да, а затем следующий камень преткновения: если вы проксируете таблицу, то обычная итерация по парам не работает. Я слышал, что это можно будет решить в Lua 5.2.
u0b34a0f6ae

В 5.2 будут метаметоды __pairs и __ipairs ... Если вы хотите сделать это в 5.1, вам придется заменить функцию pair () своей собственной. Но это, наверное, уже перебор. :-)
Александр Гладыш

3

Есть один способ, но он может вас разочаровать: использовать дополнительную переменную (или одно из полей таблицы) для хранения счетчика и увеличивать его каждый раз, когда вы делаете вставку.

count = 0
tbl = {}

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

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Другого пути нет, оператор # будет работать только с таблицами типа массивов с последовательными ключами.


3
Это можно автоматизировать с помощью прокси-таблицы и метаметодов, как упоминалось в ответе
ergosys

Из комментариев у меня сложилось впечатление, что функция proxytable / metamethods еще не полностью поддерживает этот сценарий, поэтому я приму это как лучший доступный на данный момент способ.
Роман Старков

Подсчет - единственный способ для таблиц, и добавление строк при создании таблиц лучше, чем функция подсчета их каждый раз, когда вам нужен подсчет. Вы можете добавить ключ в конце со значением, установленным для счетчика.
Henrik Erlandsson

2

Самый простой способ узнать количество записей в таблице - это использовать символ «#». #tableName получает количество записей, если они пронумерованы:

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

К сожалению, если они не пронумерованы, это не сработает.


2

Вы можете использовать библиотеку фонарика . Это имеет функциюsize которая дает фактический размер таблицы.

В нем реализовано множество функций, которые могут понадобиться нам при программировании и которые отсутствуют в Lua.

Вот пример его использования.

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

> #a
0

> tablex.size(a)
3

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)

1
При публикации ответа рекомендуется опубликовать минимальный объем кода, который напрямую отвечает на вопрос, и объяснить, как код отвечает на вопрос. Смотрите здесь .
cst1992

__newindexвызывается только при определении нового ключа, поэтому нет возможности вызвать, __newindexкогда мы устанавливаем nilсуществующий ключ.
Frank AK

-1

кажется, что когда элементы таблицы добавляются методом вставки, getn вернется правильно. В противном случае мы должны посчитать все элементы

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

Он напечатает 2 правильно

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.