首页 > 代码库 > lua中ipairs与pairs区别与注意

lua中ipairs与pairs区别与注意

 lua中基础类   lbaselib.c
 这里面定义的基础函数,函数指针数组;
static const luaL_Reg base_funcs[] = {
  {"assert", luaB_assert},
  {"collectgarbage", luaB_collectgarbage},
  {"dofile", luaB_dofile},
  {"error", luaB_error},
  {"getmetatable", luaB_getmetatable},
  {"ipairs", luaB_ipairs},
  {"loadfile", luaB_loadfile},
  {"load", luaB_load},
#if defined(LUA_COMPAT_LOADSTRING)
  {"loadstring", luaB_load},
#endif
  {"next", luaB_next},
  {"pairs", luaB_pairs},
  {"pcall", luaB_pcall},
  {"print", luaB_print},
  {"rawequal", luaB_rawequal},
  {"rawlen", luaB_rawlen},
  {"rawget", luaB_rawget},
  {"rawset", luaB_rawset},
  {"select", luaB_select},
  {"setmetatable", luaB_setmetatable},
  {"tonumber", luaB_tonumber},
  {"tostring", luaB_tostring},
  {"type", luaB_type},
  {"xpcall", luaB_xpcall},
  {NULL, NULL}
};



 这里写下常见的两个用法, ipirs 和 pairs
 1.pairs:
static int luaB_pairs (lua_State *L) {
  return pairsmeta(L, "__pairs", 0, luaB_next);
}



 lua.org中文档这样解释到,如果t有__pairs这个元方法,将table作为参数调用它,并且从调用返回第一个3个结果,否则,返回3个值,分别是:next这个方法,该 table,和一个nil所以这个结构:
     for k,v in pairs(t) do body end
 将会迭代table中所有的key-value 对

 其中next函数:
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;
  }
}



 此函数又会调用到int luaH_next (lua_State *L, Table *t, StkId key) 这个主要的函数.(前面blog有介绍)

 2.ipairs:
static int luaB_ipairs (lua_State *L) {
  return pairsmeta(L, "__ipairs", 1, ipairsaux);
}


 lua.org中文档这样解释到,如果t有__pairs这个元方法,将table作为参数调用它,并且从调用返回第一个3个结果,否则,返回3个值,分别是:iterator这个方法,该table,和一个0 所以这个结构:
     for i,v in ipairs(t) do body end
 将会迭代对(1,t[1]),(2,t[2]).......直到第一个整数key不在表中。

 对应函数:
static int ipairsaux (lua_State *L) {
  int i = luaL_checkint(L, 2);
  luaL_checktype(L, 1, LUA_TTABLE);
  i++;  /* next value */
  lua_pushinteger(L, i);
  lua_rawgeti(L, 1, i);
  return (lua_isnil(L, -1)) ? 1 : 2;
}



 这个函数中又有个关键性的函数
 const TValue *luaH_getint (Table *t, int key)   

 for循环中,pairs 每次得到的时key , value,    ipairs 是 索引值 和value, 

 通过简单粗略的分析大概明白这两个能用和不能用的情况、

 举个例子
 local test = { 1, 2, 3, 4 }
 for k , v  in ipairs(test) do
      print(k , v)
 end

 for i , v in pairs(test) do
      print(i , v)
 end

 输出结果都是
1 1
2 2
3 3
4 4


local test = {[1] = 1, [2] = 2, [3] = 3, [4] = 4}

for k , v  in ipairs(test) do
     print(k , v)
end

for i , v in pairs(test) do
     print(i , v)
end

输出结果:     
1 1
2 2 
3 3
4 4
2 2
1 1
3 3
4 4
 (这里请哪位指点下12为什么反了? 我猜是他们通过hash算法取到的主位置的关系)


 local test = {[1] = 1,  [4] = 4}

 for k , v  in ipairs(test) do
      print(k , v)
 end

 for i , v in pairs(test) do
      print(i , v)
 end

 输出结果是:
1 1
1 1
4 4
 如果test换成
 local test = {  1,  2, [3] = 3, [4] = 4}
 输出结果都是
1 1
2 2
3 3
4 4 

 换成local test = {  [‘a‘] = 1, [‘b‘] = 2, [3] = 3, [4] = 4}
 输出结果是
a 1
b 2 
3 3 
4 4

 可见这两个遍历的时候确实是受上面列出的两个主要函数的影响.ipair只能遍历table中数组部分或者下标是数字的hash表部分。而pair就是所有元素通吃。

 local test = {[1] = 1, [2] = 2, [3] = nil,  [4] = 4}
 local test = {[1] = 1, [2] = 2,  [4] = 4}
 这种 ipair会输出
1 1
2 2
 所以ipair要想所有有值得元素,下标必须是连续的整数,并且value不能有nil,而pairs就不会

 所以这里在实际操作的时候不注意,各种取长度pairs ipairs乱用就会出现奇怪的问题。

      比如上面的table
      local test = {1, 2, 3,  4}    假如是个存储别人对怪物分别的buff伤害,上满了4个,但是这个时候第3个buff失效了,那么有可能就会这么写  test[3] = nil   等遍历的时候如果用ipairs或者#遍历,就会发现怪物掉得血量会很奇怪(用#取到的长度是4),明明有长度是4,为什么只有前两个减血的值?

     所以为了避免出现这样简单但是奇怪的需要花比较长时间和精力去找错误。建议,像上面这种情况,某个值不用了,就用table.remove删去(这里需要注意在循环中使用remove 的情况,remove该值,后面的值会向前移动一位,导致后面的索引值乱掉,两个解决方案:一个是从后往前遍历表,另一个是从前往后遍历到remove得时候,将移动的i 减回来),不要使用将表中value置为nil.和遍历的时候能用pair遍历尽量用pair

lua中ipairs与pairs区别与注意