在Lua中,我应该如何处理来自C的从零开始的数组索引?

在 C 代码中,我有一个数组和一个从0开始的索引用于查找数组中的元素,例如:

char * names[] = {"Apple", "Banana", "Carrot"};
char * name = names[index];

从嵌入式 Lua 脚本中,我可以通过 getIndex() 函数访问到 index,并希望能够复制相同的数组查找。考虑到 Lua 的数组索引从1开始,是否有共识的“最佳”方法来解决这个问题呢?

例如,我可以创建一个与 C 数组相同内容的 Lua 数组,但这需要在索引时添加1:

names = {"Apple", "Banana", "Carrot"}
name = names[getIndex() + 1]

或者,我可以使用更复杂的表以避免添加1,但这会破坏 #names 这样的功能:

names = {[0] = "Apple", "Banana", "Carrot"}
name = names[getIndex()]

哪种方法更好?

编辑: 感谢迄今为止的答案。不幸的是,在 getIndex 函数中添加1的解决方案并不总是适用。因为在某些情况下,索引是“众所周知”的-也就是说,可能已经记录了索引为0表示“Apple”等情况。在这种情况下,应该首选上述的一种方法,还是有更好的解决方案呢?

编辑 2: 再次感谢答案和评论,它们真正帮助我思考这个问题。我意识到,该问题可能存在两种不同的情况,而最理想的解决方案可能因情况而异。

在第一种情况下,例如,一个数组可能随时不同,而一个索引只是相对于当前数组的。索引在代码之外没有意义。Doug Currie 和 RBerteig 是完全正确的:数组应该是从1开始的,getIndex 应该包含一个 +1。正如提到的那样,这允许 C 代码和 Lua 代码的编写都符合惯例。

第二种情况涉及具有含义的索引和可能始终相同的数组。一个极端的例子是 names 包含 "Zero","One","Two"。在这种情况下,每个索引的预期值是众所周知的,我认为使 Lua 端的索引从1开始是不直观的。我相信应该首选其他方法之一。

点赞
用户1847592
用户1847592

我更喜欢

names = {[0] = "Apple", "Banana", "Carrot"}
name = names [getIndex()]

表操作功能—— insertremovesort ——有一些是不可用的。

其他一些功能—— concat(t,sep,0)unpack(t,0) ——需要明确的起始索引才能正确运行:

print(table.concat(names, ',', 0))  --> Apple,Banana,Carrot
print(unpack(names, 0))             --> Apple   Banana  Carrot

我讨厌不断地记住+1,以迎合Lua的默认基于1的索引风格。

您的代码应反映出您的领域特定索引,以使其更易读。

如果基于0的索引非常适合您的任务,您应该在Lua中使用基于0的索引。

我喜欢Pascal中如何实现数组索引:您完全可以自由选择任何范围,例如,array [-10..-5]of byte 对于一个数组的6个元素来说是完全可以的。

2013-04-23 09:27:51
用户33252
用户33252

使用基于1的Lua表,并将 + 1 嵌入 getIndex 函数中。

2013-04-23 21:53:33
用户1442917
用户1442917

首先,这种情况不仅仅适用于混合使用 Lua 和 C 的应用程序;即使使用仅 Lua 应用程序也可能面临同样的问题。例如,我正在使用的编辑器组件从 0 开始索引行(是的,它是基于 C 的,但我仅使用其 Lua 接口),但我编辑的脚本行从 1 开始。因此,如果用户在第 3 行上设置了断点(从编辑器中开始为 0),我需要向调试器发送命令在脚本中的第 4 行上设置它(并在触发断点时进行转换)。

现在提供建议。

(1)我个人不喜欢使用 [0] 将数组强制从 0 开始索引,因为它会导致太多问题。你和 Egor 已经列出了许多问题;对我来说,最重要的是它破坏了 #ipairs

(2)在使用从 1 开始索引的数组时,我会尽量避免对它们进行索引,并尽可能使用迭代器:for i, v in ipairs(...) do 而不是 for i = 1, #array do)。

(3)我也尝试隔离处理这些转换的代码;例如,如果你正在转换编辑器中的行与脚本中的行,则编写 marker2scriptscript2marker 函数以执行转换(即使是简单的 +1 和 -1 操作)。无论是否进行 +1/-1 调整,你都需要类似的函数,只是函数的用途不同。这样做的好处是可以隐蔽这个细节。

(4)如果无法隐藏转换(我同意,+1 可能看起来很丑),那么就更明显地表示它:使用 c2ll2c 调用进行转换。在我看来,它不像 +1/-1 那么丑陋,但具有传达意图的优点,并且为你查找所有发生转换的地方提供了一种简单的方法。当你正在寻找错误或者 API 发生变化时需要更新转换逻辑时,这非常有用。

总的来说,我不会过于担心这些方面。我正在开发一个相当复杂的 Lua 应用程序,包装了多个从 0 开始索引的 C 组件,但我并没有记得因为不同的索引会导致任何问题。

2013-04-26 04:52:03
用户2130287
用户2130287

这就是 Lua 元方法和元表派上用场的地方。使用一个表代理和几个元方法,你就可以以适合你的方式修改对表的访问。

local names = {"Apple", "Banana", "Carrot"} -- 原始表
local _names = names -- 保留访问原始表的私有权限
local names = {}    -- 代理表,用于捕获对原始表的所有访问

local mt = {
  __index = function (t,k)
    return _names[k+1]   -- 访问原始表
  end,

  __newindex = function (t,k,v)
    _names[k+1] = v   -- 更新原始表
  end
}

setmetatable(names, mt)

那么这里发生了什么,原始表有一个代理,在代理里捕捉到每次对原始表的访问。当访问表时,它将以被访问的值为基础递增,模拟一个从 0 开始的数组。这是打印结果:

print(names[0]) --> Apple
print(names[1]) --> Banana
print(names[2]) --> Carrot
print(names[3]) --> nil

names[3] = "Orange" --在表中加入一个新字段
print(names[3]) --> Orange

所有表操作都像平常一样效果。通过这种方法,你不用担心与表有任何不寻常的访问。

编辑:我想指出,新的“names”表仅仅是访问原始names表的代理。所以如果查询 #names 的结果将是 nil ,因为该表本身没有任何值。您需要查询 #_names 获取原始表的大小。

编辑 2:如下评论中的 Charles Stewart 指出,您可以在 mt 表中添加 __len 元方法来确保 #names 调用给出正确的结果。

2013-04-26 16:22:26
用户577603
用户577603

为什么不在 C 数组中也将其转换为从 1 开始的数组呢?

char * names[] = {NULL, "Apple", "Banana", "Carrot"};
char * name = names[index];

坦白地说,这将在 C 端导致一些不直观的代码,但如果您坚持认为在两边都必须有“众所周知”的索引,则这似乎是最好的选择。

一种更清洁的解决方案当然是不将这些“众所周知”的索引作为接口的一部分。例如,您可以使用命名标识符而不是纯数字。枚举在 C 端非常适合此用途,而在 Lua 中,甚至可以使用字符串作为表键。

另一个可能性是将表封装在接口后面,以便用户永远不会直接访问数组,而是通过 C 函数调用来访问,该函数可以执行任意复杂的索引转换。然后您只需要在 Lua 中公开该 C 函数,就可以得到一个干净且易于维护的解决方案。

2013-04-30 15:19:14
用户222815
用户222815

为什么不将C数组呈现为Lua中的userdata呢?该技术在PiL,'Userdata'一节中有所介绍代码; 您可以设置__index__newindex__len元表方法,并且可以继承自类来提供其他序列操作功能作为常规方法(例如,使用array.removearray.sortarray.pairs函数定义一个数组,这些函数可以通过对__index进行进一步调整来定义为对象方法)。以这种方式执行操作意味着Lua和C之间没有“同步”问题,并且避免了“数组”表被视为普通表导致的差一错误的风险。

2013-05-02 06:43:17
用户673496
用户673496

你可以通过使用一个意识到不同索引基础的迭代器来修复这个 lua 错误:

function iarray(a)
  local n = 0
  local s = #a
  if a[0] ~= nil then
    n = -1
  end
  return function()
    n = n + 1
    if n <= s then return n,a[n] end
  end
end

但是,你仍然需要手动添加第零个元素:

用法示例:

myArray = {1,2,3,4,5}
myArray[0] = 0
for _,e in iarray(myArray) do
  -- 对元素 e 进行处理
end
2017-11-05 20:59:01