Làm thế nào để có được số lượng mục trong một bảng Lua?


131

Nghe giống như một câu hỏi "hãy để tôi google nó cho bạn", nhưng bằng cách nào đó tôi không thể tìm thấy câu trả lời. #Toán tử Lua chỉ đếm các mục với các khóa nguyên và cũng vậy 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"

Làm thế nào để tôi có được số lượng của tất cả các mục mà không cần đếm chúng?


3
@lhf: Tôi đã viết một bộ nối tiếp ghi nhớ mọi đối tượng mà nó đã thấy, và lần sau khi nhìn thấy nó, nó phát ra một tham chiếu số nguyên thay vì đối tượng. Cách tự nhiên để viết này là một cái gì đó giống như dictionary[value] = #dictionary + 1, nơi #đại diện cho số lượng của tất cả các đối tượng. Điều tôi thắc mắc là tại sao bạn không muốn điều này: trong tất cả các trường hợp sử dụng lành mạnh cho # (xem câu trả lời của kaizer.se), số lượng của tất cả các đối tượng chính xác bằng với những gì # đã trả về; có vẻ như làm cho # đếm mọi thứ hoàn toàn là một sự cải tiến. Tất nhiên tôi là người mới Lua và có thể bị mất điểm.
Roman Starkov

32
@lhf: Thật không hay khi bạn đặt câu hỏi về năng lực của lập trình viên bằng cách hỏi tại sao anh ta cần phải làm một cái gì đó mà tất cả các ngôn ngữ lập trình hợp lý đều có chức năng đơn giản.
Timwi

5
@Timwi: Thật không hay khi bạn nói với một trong những tác giả ngôn ngữ Lua rằng Lua không nằm trong số các ngôn ngữ lập trình "hợp lý". ;-) BTW, tôi không bao giờ cần thông tin đó.
Alexander Gladysh

5
Tôi không nghĩ rằng tôi đã từng sử dụng mọi tính năng của một ngôn ngữ. Điều đó không có nghĩa là chúng không hữu ích với người khác :)
Roman Starkov

7
@sylvanaar Theo tôi, #toán tử chỉ là định nghĩa sai. Điều này rất dễ sửa chữa: thứ nhất, đưa ra #quyết định và thứ hai, giới thiệu một toán tử hoặc hàm mới để có được số đếm. Kết thúc câu chuyện ... Tại sao họ phải bướng bỉnh như vậy? :)
La Mã Starkov

Câu trả lời:


129

Bạn đã có giải pháp trong câu hỏi - cách duy nhất là lặp lại toàn bộ bảng với pairs(..).

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

Ngoài ra, lưu ý rằng định nghĩa của toán tử "#" phức tạp hơn thế một chút. Hãy để tôi minh họa điều đó bằng cách lấy bảng này:

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

Theo hướng dẫn, bất kỳ 3, 5 và 9 là kết quả hợp lệ cho #t. Cách duy nhất để sử dụng nó là với các mảng của một phần liền kề không có giá trị nil.


42
Tôi vẫn còn rùng mình khi nhớ về trải nghiệm của mình với Lua, khi lần đầu tiên tôi nhận ra rằng giá trị trả về của một toán tử cơ bản như #không có tính quyết định.
Roman Starkov

5
Ồ, nó có khả năng quyết định. Nó chính xác giống như khi tiêu chuẩn C để lại một cái gì đó được thực hiện được xác định hành vi. Lý do là như thế này là vì những người triển khai khác nhau có thể chọn các lựa chọn thực hiện khác nhau.
Khỏa thân

19
According to the manual, any of 3, 5 and 9 are valid results for #t. Theo hướng dẫn, việc gọi # trên các chuỗi không phải là không xác định . Điều đó có nghĩa là mọi kết quả (-1, 3, 3.14, 5, 9) đều hợp lệ.
cubuspl42

6
Về kết quả hợp lệ: u0b34a0f6ae đúng với Lua 5.1, trong khi cubuspl42 đúng với Lua 5.2. Trong cả hai trường hợp, toàn bộ điều là hoàn toàn điên rồ.
Jeremy

9
Việc # trên một chuỗi không tạo ra ngoại lệ chỉ là một trong những điều khiến việc sử dụng lua hơi giống như tự cắt để cảm thấy tốt hơn.
thuyền viên

20

Bạn có thể thiết lập một bảng meta để theo dõi số lượng mục, điều này có thể nhanh hơn lặp lại nếu thông tin này là cần thiết thường xuyên.


Có một cách thuận tiện để xử lý xóa các mục với phương pháp này?
u0b34a0f6ae

Đáng buồn thay, nó xuất hiện hàm __newindex không kích hoạt các phép gán nil trừ khi chỉ mục không tồn tại, vì vậy có vẻ như bạn phải loại bỏ mục nhập kênh thông qua một chức năng đặc biệt.
ergosys

1
Bạn nên lưu trữ dữ liệu trong một bảng riêng biệt (ví dụ có thể truy cập dưới dạng giá trị tăng cho __index và __newindex). Sau đó, cả __index và __newindex sẽ kích hoạt cho mỗi lần truy cập bảng. Bạn nên kiểm tra nếu hiệu suất được chấp nhận mặc dù.
Alexander Gladysh

@Alexander: À đúng rồi, và điểm vấp ngã tiếp theo: nếu bạn ủy quyền bảng, thì việc lặp lại bình thường theo cặp không hoạt động. Điều này sẽ có thể giải quyết trong Lua 5.2, tôi đã nghe.
u0b34a0f6ae

Sẽ có __pairs và __ipairs metamethods trong 5.2 ... Nếu bạn muốn làm điều đó trong 5.1, bạn phải thay thế hàm () bằng hàm của riêng bạn. Nhưng đó có lẽ là quá nhiều. :-)
Alexander Gladysh

3

Có một cách, nhưng nó có thể gây thất vọng: sử dụng một biến bổ sung (hoặc một trong các trường của bảng) để lưu trữ số đếm và tăng nó mỗi khi bạn thực hiện chèn.

count = 0
tbl = {}

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

tbl[1] = 48
count = count + 1

print(count)   -- prints "2"

Không còn cách nào khác, toán tử # sẽ chỉ hoạt động trên các bảng giống như mảng với các khóa liên tiếp.


3
Điều này có thể được tự động hóa với một bảng proxy và siêu dữ liệu, như được đề cập bởi câu trả lời của ergosys
RBerteig

Tôi đã có ấn tượng từ các ý kiến ​​rằng điều proxytable / metamethods chưa hỗ trợ đầy đủ cho kịch bản này, vì vậy tôi sẽ chấp nhận đây là cách tốt nhất hiện có.
Roman Starkov

Đếm là cách duy nhất cho các bảng và thêm một dòng khi tạo các bảng sẽ tốt hơn một hàm để đếm chúng mỗi khi bạn cần đếm. Bạn có thể thêm một khóa ở cuối với giá trị được đặt thành số đếm.
Henrik Erlandsson

2

Cách dễ nhất mà tôi biết để có được số lượng mục trong một bảng là với '#'. #tableName nhận được số lượng mục miễn là chúng được đánh số:

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

Đáng buồn thay, nếu chúng không được đánh số, nó sẽ không hoạt động.


2

Bạn có thể sử dụng thư viện penlight . Cái này có chức năngsize cho kích thước thực tế của bảng.

Nó đã thực hiện nhiều chức năng mà chúng ta có thể cần trong khi lập trình và thiếu trong Lua.

Đây là mẫu để sử dụng nó.

> 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
Khi đăng câu trả lời, bạn nên đăng số lượng mã tối thiểu trực tiếp trả lời câu hỏi và giải thích cách mã trả lời câu hỏi. Xem ở đây .
cst1992

__newindexchỉ gọi khi một khóa mới xác định, vì vậy không có cơ hội gọi __newindexkhi chúng ta đặt nilthành một khóa tồn tại.
Frank AK

-1

dường như khi các phần tử của bảng được thêm vào bằng phương thức insert, getn sẽ trả về chính xác. Mặt khác, chúng ta phải tính tất cả các yếu tố

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

Nó sẽ in đúng 2

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.