如何在Lua中检查一张表(table)是否包含某个元素?

157

是否有一种方法可以检查表格中是否包含某个值?我有自己的(天真)函数,但我想知道是否存在一些“官方”的方法来实现这个功能?或者更高效的方法...

function table.contains(table, element)
  for _, value in pairs(table) do
    if value == element then
      return true
    end
  end
  return false
end

顺便说一下,我使用这个函数的主要原因是将表格用作集合,即不带重复元素。是否有其他可用的东西?


4
“_” 符号在这里是一个占位符,通常用来表示某些值或对象的缺失或未知状态。短横线(-)也有时用来代替下划线。 - Martin
34
这只是一个名为“_”的“垃圾”变量。pairs()返回“键,值”对,但在这个例子中,我只需要值。使用这个“_”变量来存储不需要的东西是一种惯例(在书籍《Lua 编程》中采用 http://www.lua.org/pil/index.html)。 - Wookai
我也看到过在Python和JavaScript中使用“垃圾”变量“_”命名的惯例。 - iono
5个回答

173

你可以将值放置在表的键中。例如:

function addToSet(set, key)
    set[key] = true
end

function removeFromSet(set, key)
    set[key] = nil
end

function setContains(set, key)
    return set[key] ~= nil
end

这里有一个更加完整的示例,点击这里查看。


2
也许还有 function keysOfSet(set) local ret={} for k,_ in pairs(set) do ret[#ret+1]=k end return ret end - Jesse Chisholm
我对Lua还比较陌生,想知道在创建表时该如何实现这个。它只需被创建为{keyvalue = true, keyvalue2 = true, keyvalue3 = true, [...]}吗?或者——因为表示集合可能是表的常见用途——有更简洁的方法吗? - Twisted Code
@TwistedCode 你可以使用函数来创建集合,就像这里一样。 - interjay

32

考虑到你的表示方式,你的函数已经尽可能地高效了。 当然,正如其他人所指出的那样(以及在比Lua更古老的语言中所实践的那样),解决你实际问题的方法是改变表示方式。 当你有表格并且想要集合时,你可以通过使用集合元素作为键和true作为值来将表格转换为集合。 对interjay的加分。


一种“官方”的方法可能会使用C语言来完成,这样会更快。但是,如果像你所说的那样只使用lua来完成,那么这就是最有效的方法! - somerandomdev49

4
我知道这是一篇旧文章,但我想为后人添加一些内容。处理您所遇到的问题的简单方法是创建另一个具有值和键的表格。也就是说,您有两个具有相同值的表格,一个指向一个方向,另一个指向另一个方向。
function addValue(key, value)
    if (value == nil) then
        removeKey(key)
        return
    end
    _primaryTable[key] = value
    _secodaryTable[value] = key
end

function removeKey(key)
    local value = _primaryTable[key]
    if (value == nil) then
        return
    end
    _primaryTable[key] = nil
    _secondaryTable[value] = nil
end

function getValue(key)
    return _primaryTable[key]
end

function containsValue(value)
    return _secondaryTable[value] ~= nil
end

您可以查询新表,以查看是否具有键“element”。这样可以避免需要迭代其他表的每个值。
如果结果是您实际上无法使用“element”作为键,因为它不是字符串,例如,则可以添加校验和或将其转换为字符串,然后将其用作键。
为什么要这样做?如果您的表非常大,则迭代每个元素所需的时间将是显着的,并且很难经常这样做。额外的内存开销相对较小,因为它将存储指向同一对象的2个指针,而不是同一对象的2个副本。如果您的表非常小,则重要性将会更小,事实上迭代可能甚至比进行另一个映射查找更快。
但问题的措辞强烈暗示您需要处理大量项目。

一个很好的解释,但实际上并没有为讨论增添什么。编辑interjay的答案可能是一个更好的主意。 - bcdan

3
-- in some helper module
function utils_Set(list)
    local set = {}
    for _, l in ipairs(list) do set[l] = true end
    return set
end

-- your table here
long_table = { "v1", "v2", "v1000"}

-- Consult some value
_set = utils_Set(long_table)
if _set["v1"] then print("yes!") end

1
喜欢这个是因为不需要对一个并行表进行持续的维护。 - Le Mot Juiced

2

我想不出另一种比较值的方法,但如果您将集合元素用作键,则可以将值设置为除nil以外的任何值。然后,您可以快速查找而无需搜索整个表。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接