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


在Lua, pairs 和 ipairs 可以以不同的顺序迭代相同的元素:

> 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() Lua函数产生上面显示的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的方式,那么是否有一种简单的方法可以开始使用lua_next和最后找到的整数键继续遍历表的其余部分,避免再次遍历整数键部分?这样做有两次我会看到一些整数键吗?


3289
2018-01-03 23:53


起源



答案:


您只需使用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


9
2018-01-05 14:05



谢谢。这解决了它。 - Paul


答案:


您只需使用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


9
2018-01-05 14:05



谢谢。这解决了它。 - Paul


t = {[1]=1, [2]=2, [4]=4}     -- has a length of 4

那么你的问题就在那里;没有4的长度。你可以 认为 它确实如此 #t 可能会返回4.但就Lua API而言,此表的长度是 未定义

Lua 5.1声明

表t的长度被定义为任何整数索引n,使得t [n]不是nil而t [n + 1]是nil;而且,如果t1 是零,n可以是零。对于常规数组,非n值从1到给定n,其长度恰好是n,即其最后一个值的索引。如果数组具有“空洞”(即,其他非零值之间的空值),那么#t可以是直接在nil值之前的任何索引(也就是说,它可以将任何这样的nil值视为结束的数组)。

Lua 5.2更加明确

只有当表是一个序列时才定义表t的长度,也就是说,对于某个整数n,其正数字键的集合等于{1..n}。在这种情况下,n是它的长度。注意表格就好

 {10, 20, nil, 40}

不是序列,因为它有键4但没有键3.(因此,没有n使得集合{1..n}等于该表的正数字键集。)但请注意,非数字键不会干扰表是否为序列。

但在这两种情况下,长度都是 未定义


3
2018-01-04 00:53



是的,我理解表长度有时未定义的性质,但我不明白这有助于回答我的问题:“如何产生 ipairs C API中的行为?“就像我说的,我 天真 可以使用报告的长度,但我怀疑在ipairs的封面下有更复杂的东西。 - Paul
我认为Nicol Bolas的观点是,最好的做法是完全忽视#操作符 - 你称之为报告的长度 - 除非它满足作为序列的严格要求。在问题的最后部分,你可以 不 使用 lua_next 继续像ipairs一样遍历成对的遍历。 lua_next 不能保证避免已经遍历的密钥 rawgeti 由daurnimator给出的循环,如果您尝试以此方式将其用作循环延续,也可以跳过键。 - Tyler