Lua中对table进行排序

0x00 table.sort
lua的table中实现了table.sort()方法,可以对table进行排序。这种情况适用于直接以table存储的元素为依据进行的排序。

local tbl = {1,2,4,5,3}

function sortGT(a, b)
    return a > b
end

print('before sort.')
print_r(tbl)
table.sort(tbl)
print('after less than sort.')
print_r(tbl)
table.sort(tbl, sortGT)
print('after greater than sort.')
print_r(tbl)

table.sort(tbl, comp),comp在缺省的情况下默认以小于进行排序。
输出结果为:

before sort.
table: 0075B440 {
  [1] => 1
  [2] => 2
  [3] => 4
  [4] => 5
  [5] => 3
}

after less than sort.
table: 0075B440 {
  [1] => 1
  [2] => 2
  [3] => 3
  [4] => 4
  [5] => 5
}

after greater than sort.
table: 0075B440 {
  [1] => 5
  [2] => 4
  [3] => 3
  [4] => 2
  [5] => 1
}

0x01 复杂table的排序
如果table中存储的数据不是数值,而是嵌套了table结构,那么上述方法就无能为力了。比如形如:

local tbl = {[1] = 40303, [3] = 40304, [5] = 40305, [7] = 40301, [9] = 40302}

这样的table,想以嵌套table的value进行排序,而不是key,直接使用上述的排序方法并没有作用。两个原因:
首先,由于传进sort方法的参数为table,默认的排序方法是对数值进行排序的;
其次,默认的方法无法取得传入参数的value进行排序。
由于上述tbl的索引是无序的,我们首先应对tbl进行排序。

local tbl = {[1] = 40303, [3] = 40304, [5] = 40305, [7] = 40301, [9] = 40302}

-- 对table的索引进行重新组织,使之成为连续的数值,参考《Lua程序设计》
function pairsBySort(_t, func)
    local a = {}
    for n in pairs(_t) do a[#a + 1] = n end
    table.sort(a, func)
    local i = 0
    return function()
        i = i + 1
        return a[i], _t[a[i]]
    end
end

print_r(tbl)
for i,v in pairsBySort(tbl) do
    tbl_ret[#tbl_ret + 1] = v
end
print_r(tbl_ret)

运行结果如下:

table: 004DB6C0 {
  [7] => 40301
  [3] => 40304
  [1] => 40303
  [5] => 40305
  [9] => 40302
}

table: 004DB698 {
  [1] => 40303
  [2] => 40304
  [3] => 40305
  [4] => 40301
  [5] => 40302
}

下面需要解决第二个问题,即“默认的方法无法取得传入参数的value进行排序。”添加排序函数

function sortFunc(a, b)
    return tbl[a] < tbl[b]
end

print_r(tbl)
for i,v in pairsBySort(tbl, sortFunc) do
    tbl_ret[#tbl_ret + 1] = v
end
print_r(tbl_ret)

运行结果:

table: 004CB6C0 {
  [7] => 40301
  [3] => 40304
  [1] => 40303
  [5] => 40305
  [9] => 40302
}

table: 004CB698 {
  [1] => 40301
  [2] => 40302
  [3] => 40303
  [4] => 40304
  [5] => 40305
}

0x02 table.sort的报错
有时候写了自定义的sortFunc(a,b)运行时会报错:

 attempt to compare number with nil和invalid order function for sorting

需要特别注意SortFunc(a,b)中对于a == b 的情况的处理,一定要返回false,否则会引起上述的错误,究其原因,就需要研究一下lua的排序规则了:
http://www.lua.org/source/5.1/ltablib.c.html

static void auxsort (lua_State *L, int l, int u) {
  while (l < u) {  /* for tail recursion */
    int i, j;
    /* sort elements a[l], a[(l+u)/2] and a[u] */
    lua_rawgeti(L, 1, l);
    lua_rawgeti(L, 1, u);
    if (sort_comp(L, -1, -2))  /* a[u] < a[l]? */
      set2(L, l, u);  /* swap a[l] - a[u] */
    else
      lua_pop(L, 2);
    if (u-l == 1) break;  /* only 2 elements */
    i = (l+u)/2;
    lua_rawgeti(L, 1, i);
    lua_rawgeti(L, 1, l);
    if (sort_comp(L, -2, -1))  /* a[i]<a[l]? */
      set2(L, i, l);
    else {
      lua_pop(L, 1);  /* remove a[l] */
      lua_rawgeti(L, 1, u);
      if (sort_comp(L, -1, -2))  /* a[u]<a[i]? */
        set2(L, i, u);
      else
        lua_pop(L, 2);
    }
    if (u-l == 2) break;  /* only 3 elements */
    lua_rawgeti(L, 1, i);  /* Pivot */
    lua_pushvalue(L, -1);
    lua_rawgeti(L, 1, u-1);
    set2(L, i, u-1);
    /* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */
    i = l; j = u-1;
    for (;;) {  /* invariant: a[l..i] <= P <= a[j..u] */
      /* repeat ++i until a[i] >= P */
      while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) {
        if (i>u) luaL_error(L, "invalid order function for sorting");
        lua_pop(L, 1);  /* remove a[i] */
      }
      /* repeat --j until a[j] <= P */
      while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) {
        if (j<l) luaL_error(L, "invalid order function for sorting");
        lua_pop(L, 1);  /* remove a[j] */
      }
      if (j<i) {
        lua_pop(L, 3);  /* pop pivot, a[i], a[j] */
        break;
      }
      set2(L, i, j);
    }
    lua_rawgeti(L, 1, u-1);
    lua_rawgeti(L, 1, i);
    set2(L, u-1, i);  /* swap pivot (a[u-1]) with a[i] */
    /* a[l..i-1] <= a[i] == P <= a[i+1..u] */
    /* adjust so that smaller half is in [j..i] and larger one in [l..u] */
    if (i-l < u-i) {
      j=l; i=i-1; l=i+2;
    }
    else {
      j=i+1; i=u; u=j-2;
    }
    auxsort(L, j, i);  /* call recursively the smaller one */
  }  /* repeat the routine for the larger one */
}

static int sort (lua_State *L) {
  int n = aux_getn(L, 1);
  luaL_checkstack(L, 40, "");  /* assume array is smaller than 2^40 */
  if (!lua_isnoneornil(L, 2))  /* is there a 2nd argument? */
    luaL_checktype(L, 2, LUA_TFUNCTION);
  lua_settop(L, 2);  /* make sure there is two arguments */
  auxsort(L, 1, n);
  return 0;
}

从代码中可以看到,lua是使用quick sort作为底层排序规则的,快排作为一种不稳定排序,对于比较元素相同时的处理尤为需要注意。
从报错的log可以定位到报错的位置位于

for (;;) {  /* invariant: a[l..i] <= P <= a[j..u] */

可以看到是边界判断的地方出了问题。推断lua的实现为了效率考虑,假定传入的排序函数在遇到a == b 的时候返回false,因而底层的排序函数没有对这种情况进行严格的边界检查。

0x03 相关资料
http://pkxpp.github.io/2016/07/26/lua%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0(6)table.sort/
http://blog.csdn.net/David_Dai_1108/article/details/46434787
http://www.cnblogs.com/slysky/p/5360387.html

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值