泛型for
首先是泛型for的语法
for var-list in exp-list do
body
end
当执行for循环时候,泛型for会接收exp-list返回的三个值分别用作迭代函数、不可变状态和控制变量的初始值。上述步骤完成后泛型for使用不可变状态和控制变量为参数来调用迭代函数,泛型for将迭代函数的返回值赋给变量列表中声明的变量,如果第一个返回值(赋给控制变量的值)为 nil ,那么循环终止;否则,泛型for执行它的循环体并再次调用迭代函数,再不断地重复这个过程。
伪代码是这样的:
local _f, _s, _var = exp-list
while true do
local var_1, ... , var_n = _f(_s, _var)
_var = var_1
if _var == nil then break end
block
end
而ipairs和pairs是lua提供的两个迭代函数生成器也就是前面的exp-list
ipairs的实现
/*
** Traversal function for 'ipairs'
*/
static int ipairsaux (lua_State *L) {
lua_Integer i = luaL_checkinteger(L, 2);
// i = i + 1
i = luaL_intop(+, i, 1);
lua_pushinteger(L, i);
// table[i] == nil and return 1 or return 2
return (lua_geti(L, 1, i) == LUA_TNIL) ? 1 : 2;
}
/*
** 'ipairs' function. Returns 'ipairsaux', given "table", 0.
** (The given "table" may not be a table.)
*/
static int luaB_ipairs (lua_State *L) {
luaL_checkany(L, 1);
lua_pushcfunction(L, ipairsaux); /* iteration function */
lua_pushvalue(L, 1); /* state */
lua_pushinteger(L, 0); /* initial value */
return 3;
}
当我们使用ipairs的时候会调用luaB_ipairs,可以看到ipairs执行之后会返回三个参数,第一个参数是个遍历函数ipairsaux,第二个是ipairs函数调用时候传入的table,第三个参数是0。ipairs的遍历函数是通过 i 作为table的key,不断自增 i 从而达到遍历的效果的。
可以理解为如下代码:
local iter = function(t, i)
i = i + 1
local v = t[i]
if v then
return i, v
end
end
function ipairs(t)
return iter, t, 0
end
pairs的实现
static int luaB_next (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, 1))
return 2;
else {
lua_pushnil(L);
return 1;
}
}
static int pairscont (lua_State *L, int status, lua_KContext k) {
(void)L; (void)status; (void)k; /* unused */
return 3;
}
static int luaB_pairs (lua_State *L) {
luaL_checkany(L, 1);
if (luaL_getmetafield(L, 1, "__pairs") == LUA_TNIL) { /* no metamethod? */
lua_pushcfunction(L, luaB_next); /* will return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* and initial value */
}
else {
lua_pushvalue(L, 1); /* argument 'self' to metamethod */
lua_callk(L, 1, 3, 0, pairscont); /* get 3 values from metamethod */
}
return 3;
}
当我们使用pairs的时候会调用luaB_pairs,在没有__pairs元方法的时候,可以看到pairs执行之后返回三个参数,第一个参数是个遍历函数luaB_next,第二个是pairs函数调用时候传入的table,第三个参数是nil。这里的luaB_pairs使用的就是遍历函数lua_next就是我们常用的next函数,也就是说pairs是通过不断调用next函数达到遍历的效果的。
可以理解为如下代码:
function pairs(t)
return next, t, nil
end
两者区别
由他们的实现可以看出两者的区别
ipairs是没法保证完整遍历的,优势是可以有序遍历。
pairs是由于next的机制可以保证完整的遍历,但是却没办法保证顺序。
-- 遇table1[3]==nil时终止 ipairs可遍历到:a, b pairs可遍历到:a, b, d
local table1 = { "a", "b", nil, "d" }
-- ipairs可遍历到:a, b, d pairs可遍历到:a, b, d
local table2 = { "a", "b", c = 1, "d" }