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