【问题标题】:How to get number of entries in a Lua table?如何获取 Lua 表中的条目数?
【发布时间】:2011-02-11 22:39:12
【问题描述】:

听起来像是一个“让我为你搜索”的问题,但不知何故我找不到答案。 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"

如何在不计算的情况下获得所有个条目的数量?

【问题讨论】:

  • @lhf:我编写了一个序列化程序,它会记住它看到的每个对象,并且下次看到它时它会发出一个整数引用而不是对象。写这个的自然方式类似于dictionary[value] = #dictionary + 1,其中# 表示所有对象的数量。 想知道为什么你想要这个:在所有 sane 用例中#(参见 kaizer.se 的回答),所有对象的计数完全等于 # 已经返回的;似乎让 #count everything 严格来说是一种改进。当然,我是 Lua 新手,可能没有抓住重点。
  • @lhf 另一个使用示例:将大量数据检索到一个表中,其中每个数据项都有一个唯一的字符串标识符。我使用这个标识符作为键,因为我稍后会用它来查找。我现在想打印处理的数据项的数量。我必须保留一个计数器并为每一行手动增加它。当然不是什么大不了的事,但不数数就不能说出这样的事情是不寻常的,因为你问“为什么”...... :)
  • 当用作容器时,该表是保存有关当前对象计数信息的最佳位置。例如,当表格用作 Set 时。
  • @lhf:我还有一个用例,我需要知道数字,在这种情况下,我需要知道表格中是否只剩下一个项目,在这种情况下我会处理它与如果有很多项目不同。如果答案是我们计算它们,那很好;我猜一个刚刚得到答案的函数会降低我们在其他地方的性能(这样的功能可能需要 lua 每次我们设置表值时都测试新旧值是否为 nil,然后相应地更新计数器)
  • @Alternator,要测试表中是否正好有一对,请使用next(t)~=nil and next(next(t))==nil

标签: lua


【解决方案1】:

您已经有了问题中的解决方案——唯一的方法是使用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 值的连续部分的数组。

【讨论】:

  • 好吧,他说“不计算它们”,但可能没有其他办法
  • According to the manual, any of 3, 5 and 9 are valid results for #t。根据手册,在非序列上调用 # 是 undefined。这意味着 any 结果 (-1, 3, 3.14, 5, 9) 是有效的。
  • 关于有效结果:u0b34a0f6ae 对于 Lua 5.1 是正确的,而 cubuspl42 对于 Lua 5.2 是正确的。无论哪种情况,整个事情都是完全疯狂的。
  • 最好有一个基于reduce的基本fp实用程序:github.com/sarimarton/.config/blob/master/hammerspoon/util/…
【解决方案2】:

您可以设置一个元表来跟踪条目的数量,如果经常需要此信息,这可能比迭代更快。

【讨论】:

  • 有没有一种方便的方法来处理使用这种方法擦除条目?
  • 遗憾的是,除非索引不存在,否则 __newindex 函数似乎不会在 nil 分配上触发,因此您似乎必须通过特殊函数来删除条目。
  • 您应该将数据存储在单独的表中(例如可作为 __index 和 __newindex 的上值访问)。然后 __index 和 __newindex 都会为每个表访问触发。不过,您应该检查性能是否可以接受。
  • @Alexander:啊,是的,然后是下一个绊脚石:如果您代理表,那么按对的正常迭代不起作用。听说这可以在 Lua 5.2 中解决。
  • 在 5.2 中会有 __pairs 和 __ipairs 元方法......如果你想在 5.1 中这样做,你必须用你自己的替换 pairs() 函数。但这可能太多了。 :-)
【解决方案3】:

我所知道的获取表中条目数的最简单方法是使用“#”。 #tableName 获取条目的数量,只要它们被编号:

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

遗憾的是,如果它们没有编号,它将无法正常工作。

【讨论】:

    【解决方案4】:

    有一种方法,但可能会令人失望:使用附加变量(或表的字段之一)来存储计数,并在每次插入时增加它。

    count = 0
    tbl = {}
    
    tbl["test"] = 47
    count = count + 1
    
    tbl[1] = 48
    count = count + 1
    
    print(count)   -- prints "2"
    

    别无他法,# 运算符仅适用于具有连续键的类数组表。

    【讨论】:

    • 这可以通过代理表和元方法自动化,正如ergosys's answer所提到的那样
    • 我从 cmets 得到的印象是 proxytable/metamethods 东西还不完全支持这种情况,所以我会接受这是目前可用的最佳方式。
    • 计数是表格的唯一方法,在创建表格时添加行比每次需要计数时都计数的函数要好。您可以在末尾添加一个键,并将值设置为计数。
    【解决方案5】:

    您可以使用penlight library。这有一个函数size,它给出了表格的实际大小。

    它实现了许多我们在 Lua 中编程时可能需要和缺少的功能。

    这是使用它的示例。

    > tablex = require "pl.tablex"
    > a = {}
    > a[2] = 2
    > a[3] = 3 
    > a['blah'] = 24
    
    > #a
    0
    
    > tablex.size(a)
    3
    

    【讨论】:

    • 是的,但所做的只是for k in pairs(t) do i = i + 1 end,所以它并不比这个主题上的其他选项更好,并且增加了使用另一个库的额外开销
    【解决方案6】:
    function GetTableLng(tbl)
      local getN = 0
      for n in pairs(tbl) do 
        getN = getN + 1 
      end
      return getN
    end
    

    你是对的。没有其他方法可以获取表格长度

    【讨论】:

      【解决方案7】:
      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)
      

      【讨论】:

      • __newindex 仅在定义新键时调用,因此当我们将nil 设置为存在键时,没有机会调用__newindex
      【解决方案8】:

      似乎当通过insert方法添加表格的元素时,getn会正确返回。否则,我们必须计算所有元素

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

      它会正确打印 2

      【讨论】:

        【解决方案9】:

        我偶然发现了这个线程并想发布另一个选项。我正在使用从块控制器生成的 Luad,但它本质上是通过检查表中的值,然后将要检查的值增加 1 来工作的。最终,表将用完,并且该索引处的值将为零。

        所以从返回 nil 的索引中减去 1,这就是表的大小。

        我有一个 TableSize 的全局变量,它设置为这个计数的结果。

        function Check_Table_Size()
          local Count = 1
          local CurrentVal = (CueNames[tonumber(Count)])
          local repeating = true
          print(Count)
          while repeating == true do
            if CurrentVal ~= nil then
              Count = Count + 1
              CurrentVal = CueNames[tonumber(Count)]
             else
              repeating = false
              TableSize = Count - 1
            end
          end
          print(TableSize)
        end
        

        【讨论】:

          猜你喜欢
          • 2022-01-15
          • 1970-01-01
          • 2011-07-15
          • 2018-06-02
          • 2014-02-09
          • 2019-10-20
          • 2011-11-09
          • 1970-01-01
          相关资源
          最近更新 更多