如何在 C API 中生成 ipairs(而不是 pairs)行为

在Lua中,pairsipairs可以以不同的顺序遍历相同的元素:

> t = {[1]=1, [2]=2, [3]=3}
> for k,v in pairs(t) do print(k,v) end
2       2
1       1
3       3
> for k,v in ipairs(t) do print(k,v) end
1       1
2       2
3       3

使用C API时,我只看到一个用于遍历表格的工具:lua_next()函数,它的行为非常像pairs()函数,这会产生上述2-1-3顺序。

我正在寻找一种高效的C方法,以顺序迭代表格中的整数键(具有类似于ipairs的C API版本)。

天真地,我考虑了:

int tableLength = luaL_len(L, tableIndex);
for (i=0, i++, i>tableLength){
    // if t[i] is not null ...
}

但是,当表格大小不与连续整数键的数量相匹配时,我不清楚可能存在的性能问题:

t = {[1]=1, [2]=2, [4]=4}     -- has a (reported) length of 4
t = {[1]=1, [2]=2, [40000]=4} -- has a (reported) length of 2

如果这确实是ipairs所做的方式,那么是否有一种简单的方法来使用上一个找到的整数键开始继续遍历其余的表格,避免再次遍历整数键部分?通过这样做,有可能我会看到某些整数键两次吗?

点赞
用户734069
用户734069
t = {[1]=1, [2]=2, [4]=4}     -- 这个表看起来长度为 4

好吧,你的问题就在这里;这个表并不是长度为 4 的。你或许_认为_它是,而且 #t 可能会返回 4。但是在 Lua API 中,这个表的长度是_未定义_的。

Lua 5.1 的文档

一个表 t 的长度被定义为任何整数下标 n,它满足 t[n] 不为 nil,且 t[n+1] 为 nil;并且,如果 t[1] 是 nil,那么 n 可以为 0。对于一个普通数组,包含从 1 开始直到给定 n 的非 nil 值,它的长度就是 n,也就是它最后一个值的下标。如果数组中有 “空洞” (即,在其他非 nil 值之间有 nil 值),那么 #t 可以是任何那些直接在 nil 值之前的下标(也就是,它可以将任何这样的 nil 值视为数组的结尾)。

Lua 5.2 更加明确地说明了

一个表 t 的长度仅在该表是序列时才被定义,也就是说,它的正整数键的集合等于 {1..n},其中 n 是一个整数。在这种情况下,n 就是它的长度。但是要注意,像

 {10, 20, nil, 40}

这样的表不是序列,因为它虽然有键 4,但没有键 3。(因此,不存在一个 n,使得集合 {1..n} 和该表的正整数键的集合相等。)但是,非数值键不会影响一个表是否为序列。

但是在这两种情况下,这个表的长度都是_未定义_的。

2013-01-04 00:53:36
用户282536
用户282536

你只需要使用 rawgeti 直到获取到 nil 键:

// Tabs is on top of stack
for ( int i=1 ; ; i++ ) {
    lua_rawgeti(L,-1,i);
    if ( lua_isnil(L,-1) ) {
        lua_pop(L,1);
        break;
    }
    /* Do something */
    lua_pop(L,1);
}

从源代码中可以看出,这正是 ipairs 内部所做的操作: http://www.lua.org/source/5.1/lbaselib.c.html#ipairsaux

2013-01-05 14:05:12