Як отримати кількість записів у таблиці Lua?


132

Здається, питання "дозвольте мені погуглювати це для вас", але я чомусь не можу знайти відповідь. Луа# рахує записи лише з цілими ключами, і це робить 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), підрахунок всіх об'єктів в точності дорівнює тому , що вже # повернень; здається, що зробити # рахувати все - це суто вдосконалення. Звичайно, я новачок Луа і, можливо, не вистачає суті.
Роман Старков

32
@lhf: Вам не приємно ставити під сумнів компетенцію програміста, запитуючи, чому йому потрібно робити щось, для чого всі розумні мови програмування мають просту функцію.
Тімві

5
@Timwi: Не приємно вам сказати одному з авторів мови Lua, що Lua не входить до "розумних" мов програмування. ;-) BTW, мені також ніколи не потрібна ця інформація.
Олександр Гладиш

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. Єдиний розумний спосіб його використання - це масиви однієї суміжної частини без нульових значень.


42
Я все ще здригаюся від пам’яті свого досвіду роботи з Lua, коли я вперше зрозумів, що повернене значення базового оператора на зразок #не є детермінованим.
Роман Старков

5
О, це, певно, детерміновано. Це точно те ж саме, що і коли стандарт C залишає щось для визначеної поведінкою поведінки. Причина такої в тому, що різні виконавці можуть вибирати різні варіанти впровадження.
Голий

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 не спрацьовує при призначенні нульових значень, якщо індекс не існує, тому, здається, вам доведеться усунути введення послідовності через спеціальну функцію.
ergosys

1
Ви повинні зберігати дані в окремій таблиці (наприклад, доступній як оновлення для __index та __newindex). Тоді і __index, і __newindex запускаються для кожного доступу до таблиці. Слід перевірити, чи продуктивність є прийнятною.
Олександр Гладиш

@Alexander: Ага так, і наступний момент спотикання: якщо ви проксі-сервер таблиці, то нормальна ітерація парами не працює. Це можна було вирішити в Луї 5.2, я чув.
u0b34a0f6ae

Існують метаметоди __pairs та __ipairs в 5.2 ... Якщо ви хочете зробити це в 5.1, вам доведеться замінити функцію пар () на власну. Але це, мабуть, занадто багато. :-)
Олександр Гладиш

3

Є один спосіб, але це може бути невтішним: використовуйте додаткову змінну (або одне з полі таблиці) для зберігання рахунку та збільшуйте його щоразу, коли ви робите вставку.

count = 0
tbl = {}

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

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Немає іншого способу, # оператор буде працювати лише над таблицями, схожими на масив, з послідовними ключами.


3
Це можна автоматизувати за допомогою таблиці проксі та метаметодів, як згадується у відповіді
ergosys

У мене склалося враження, що коментарі про те, що програма proxytable / metamethods ще не повністю підтримує цей сценарій, тому я прийму це як найкращий доступний спосіб.
Роман Старков

Підрахунок - це єдиний спосіб для таблиць, а додавання рядків при створенні таблиць краще, ніж функція для їх підрахунку кожного разу, коли вам потрібно підрахунок. Ви можете додати ключ в кінці зі значенням, встановленим для підрахунку.
Генрік Ерландссон

2

Найпростіший спосіб, за яким я знаю, отримати кількість записів у таблиці - це "#". #tableName отримує кількість записів, якщо вони пронумеровані:

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

На жаль, якщо вони не будуть пронумеровані, це не спрацює.


2

Ви можете користуватися бібліотекою penlight . Це має функцію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ключ існування.
Френк АК

-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.