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

时间:2013-04-23 08:07:08

标签: c lua lua-table

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

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

从嵌入式Lua脚本中,我可以通过index函数访问getIndex(),并希望复制数组查找。鉴于Lua的基于单一的数组,是否有一个同意“最佳”的方法呢?

例如,我可以创建一个Lua数组,其内容与我的C数组相同,但这需要在索引时添加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端的索引设置为一个是不直观的。我认为应该首选其他方法之一。

7 个答案:

答案 0 :(得分:16)

使用基于1的Lua表,并将+ 1隐藏在getIndex函数内。

答案 1 :(得分:6)

我更喜欢

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个元素的数组是绝对正常的。

答案 2 :(得分:6)

首先,这种情况并不是混合使用Lua和C的应用程序所独有的;即使仅使用Lua应用程序,您也可以面对同样的问题。举一个例子,我正在使用一个编辑器组件来索引从0开始的行(是的,它是基于C的,但我只使用它的Lua接口),但是我在编辑器中编辑的脚本中的行是1-根据。因此,如果用户在第3行设置断点(从编辑器中的0开始),我需要向调试器发送一个命令,将其设置在脚本的第4行(并在命中断点时转换回来)。 / p>

现在的建议。

(1)我个人不喜欢使用[0] hack进行数组操作,因为它打破了很多东西。你和叶戈尔已经列出了很多;对我来说最重要的是它会中断#ipairs

(2)当使用基于1的数组时,我尽量避免索引它们并尽可能使用迭代器:for i, v in ipairs(...) do而不是for i = 1, #array do)。

(3)我还尝试隔离处理这些转换的代码;例如,如果要在编辑器中的行之间进行转换以管理脚本中的标记和行,那么使用marker2scriptscript2marker函数进行转换(即使它是简单的+1和-1操作)。无论如何,即使没有+ 1 / -1调整,你也会有这样的东西,它只是隐含的。

(4)如果你无法隐藏转换(并且我同意,+1可能看起来很难看),那么使其更加引人注目:使用进行转换的c2l和l2c调用。在我看来,它不像+ 1 / -1那样丑陋,但具有传达意图的优势,并且还为您提供了一种简单的方法来搜索转换发生的所有位置。当您寻找一个错误或API更改导致更新此逻辑时,它非常有用。

总的来说,我不会太担心这些方面。我正在开发一个相当复杂的Lua应用程序,它包含几个基于0的C组件,并且不记得由不同的索引引起的任何问题...

答案 3 :(得分:6)

这是Lua metemethods和metatables派上用场的地方。使用表代理和几个元方法,您可以以适合您需要的方式修改对表的访问。

local names = {"Apple", "Banana", "Carrot"} -- Original Table
local _names = names -- Keep private access to the table
local names = {}    -- Proxy table, used to capture all accesses to the original table

local mt = {
  __index = function (t,k)
    return _names[k+1]   -- Access the original table
  end,

  __newindex = function (t,k,v)
    _names[k+1] = v   -- Update original table
  end
}

setmetatable(names, mt)  

所以这里发生的是,原始表有自己的代理,然后代理捕获表中的每次访问尝试。访问表时,它会增加访问的值,模拟基于0的数组。以下是打印结果:

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

names[3] = "Orange" --Add a new field to the table
print(names[3]) --> Orange

所有表操作都像通常一样。使用这种方法,您不必担心会破坏对表的任何非常访问。

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

编辑2:正如Charles Stewart在下面的评论中指出的那样,你可以在mt表中添加一个__len元方法,以确保#names调用能够为你提供正确的结果。

答案 4 :(得分:4)

为什么不将C阵列转换为基于1的阵列呢?

char * names[] = {NULL, "Apple", "Banana", "Carrot"};
char * name = names[index];
坦率地说,这将导致C方面的一些不直观的代码,但如果你坚持认为必须有着名的'在双方都有效的指数,这似乎是最好的选择。

更清洁的解决方案当然不是要让那些众所周知的' indices的接口部分。例如,您可以使用命名标识符而不是普通数字。枚举在C方面是很好的匹配,而在Lua中你甚至可以使用字符串作为表键。

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

答案 5 :(得分:3)

为什么不将您的C数组作为userdata呈现给Lua?该技术用PiL, section 'Userdata'中的代码描述;您可以设置__index__newindex__len metatable方法,并且可以从类继承以提供其他序列操作函数作为常规方法(例如,使用{{定义数组) 1}},array.removearray.sort函数,可以通过进一步调整array.pairs来定义为对象方法。以这种方式做事意味着Lua和C之间没有“同步”问题,并且它避免了“数组”表被视为普通表导致逐个错误的风险。

答案 6 :(得分:0)

你可以通过使用知道不同索引库的迭代器来修复这个lua-flaw:

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

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

用法示例:

myArray = {1,2,3,4,5}
myArray[0] = 0
for _,e in iarray(myArray) do
  -- do something with element e
end