Lua:如何在键为表格(或对象)的表格中查找

我想存储一个 lua 表,其中的键是其他 lua 表。我知道这是可能的,但我想能够在表中使用那些表的副本进行查找。具体而言,我想能够执行以下操作:

t = {}
key = { a = "a" }
t[key] = 4
key2 = { a = "a" }

然后我想能够查找:

t[key2]

并得到 4。

我知道我可以将 key 转换为字符串并将其放入表 t 中。我也考虑过编写自定义哈希函数或通过嵌套表来完成此操作。有什么最好的方法可以让我获得这种类型的功能?我还有哪些选项?

原文链接 https://stackoverflow.com/questions/9201601

点赞
stackoverflow用户582
stackoverflow用户582

我不确定你能否做到这一点。你可以使用metatable来为表定义相等性,但是没有办法定义哈希函数,我怀疑仅仅定义相等性并不能满足你的需求。显然,你可以定义相等性,然后使用pairs()迭代表并自行比较键,但这会将本应该是O(1)的查找变成O(n)

2012-02-08 21:27:05
stackoverflow用户221509
stackoverflow用户221509

在 Lua 中不可能做到这一点。如果您将表格用作键,则该键是该特定表格的“实例”。即使您使用相同内容创建不同的表格,该实例也不同,因此它是不同的键。

如果您想要做这样的事情,可以创建一种哈希函数,遍历表格以用作键(在需要的情况下甚至可以进行递归),并构造表格内容的字符串表示形式。它不需要可读性,只要在具有不同内容的情况下不同并且对于具有相同内容的表格相同即可。除了使用 pairs() 遍历表格外,您还需要将键插入到表格中,并使用 table.sort() 对其进行排序,因为 pairs() 将以任意顺序返回它们,并且您希望相同的字符串用于“相等”的表格。

一旦构造出这样的字符串,您就可以将其用作键:

function hash(t) ... end
t = {}
key1 = { a = "a", b = "b" }
t[hash(key1)] = 4
key2 = { a = "a", b = "b" }
print(t[hash(key2)]) -- 如果哈希函数工作正常,则应打印“4”

在我看来,这一切对于索引的简单任务来说太复杂了,您可能需要重新考虑使用表的副本进行索引的愿望。为什么您想要这样的功能呢?

更新

如果您只需要处理短语,那么我认为连接它们比创建这样的通用哈希函数更容易。如果您需要用于短语序列,则实际上不需要迭代表格并对键进行排序,只需从每个短语中收集主要信息即可。您仍需要使用辅助函数,它可以为您创建一个合适的键:

function pkey(...)
    local n, args = select('#', ...), { ... }
    for i=1,n do args[i] = args[i].value end -- 在此处提取您的信息
    return table.concat(args, ' ') -- 空格或其他分隔符,例如:
end
tab[pkey(phrase1, phrase2, phrase3)] = "value"
2012-02-08 22:28:48
stackoverflow用户312586
stackoverflow用户312586

在Lua中,分别创建的两个表被认为是"不同"的。但如果你创建了一个表,你可以将它分配给任何你想要的变量,当你比较它们时,Lua会告诉你它们是相等的。换句话说:

t = {}
key = { a = "a" }
t[key] = 4
key2 = key
...
t[key2] -- 返回4

所以,这就是做你想做的事情的简单,干净的方法。将key存储在某个地方,以便您可以通过使用它来检索4。这也非常快速。

如果你_真的不想这样做……好吧,有一种方法。但它有点低效和丑陋。

第一部分是制作一个函数,比较两个单独的表。如果两个表是"等效"的,则返回true,否则返回false。我们称之为等效函数。它应该像这样工作:

等效({a=1},{a=1})          -- true
等效({a=1,b=2}, {a=1})     -- false
等效({a={b=1}}, {a={b=2}}) -- false

该函数必须是递归的,以处理包含表本身的表。如果其中一个表"包含"另一个表但具有更多元素,则它也不能被欺骗。我提出了这个实现;可能有更好的。

local function equivalent(a,b)
  if type(a) ~= 'table' then return a == b end

  local counta, countb = 0, 0

  for k,va in pairs(a) do
    if not equivalent(va, b[k]) then return false end
    counta = counta + 1
  end

  for _,_ in pairs(b) do countb = countb + 1 end

  return counta == countb
end

我不会在这里解释该函数。我希望它很清楚它做什么。

拼图的另一部分包括使t在比较键时使用equivalent函数。这可以通过谨慎的元表操作和额外的"存储"表来实现。

我们基本上将t转化为一个冒充者。当我们的代码告诉它在一个键下存储一个值时,它不会将它保存在自己身上;相反,它将它给存储到额外的表中(我们将其称为store)。当代码请求t的一个值时,它会在store中查找,但使用equivalent函数来获取它。

这是代码:

local function equivalent(a,b)
... --与先前相同的代码
end

local store = {} --这是存储值的表

t = setmetatable({}, {
  __newindex = store,
  __index = function(tbl, key)
    for k,v in pairs(store) do
      if equivalent(k,key) then return v end
    end
  end
})

使用示例:

t[{a = 1}] = 4

print(t[{a = 1}]) -- 4
print(t[{a = 1, b = 2}]) -- nil
2012-02-08 22:41:25
stackoverflow用户1211342
stackoverflow用户1211342

我对语言处理不太了解,不知道你的程序想要达到什么目的,但是怎么样收集词元呢?可以使用嵌套表结构,如索引表仅存储以第一个短语词元为索引的表,然后每个子表包含以第二个短语词元索引的值,依此类推,直到到达短语最终词元,将为索引的数字值对应短语出现的次数。

如果你有以下两个短语:

  • 我喜欢香蕉。
  • 我喜欢辣妹子。

你的索引将具有以下结构:

index["I"] = {
    ["like"] = {
        ["banana"] = 1,
        ["hot"] = {
            ["chick"] = 1
        }
    }
}

这样你可以在一次遍历中计算频率,并在索引时同时统计出现次数,但如我之前所说,这取决于你的目标,并且它将意味着重新分割你的短语,以便通过你的索引找到出现次数。

2012-02-16 20:45:53