问题描述
lua table.sort 排序报错,invalid order function for sorting stack traceback
测试代码
结论
根据测试结果,出现lua error需要满足以下条件
- table中元素数量大于3
- table中元素存在相等情况
- 当元素相等时,排序方法返回true
- 排序算法不稳定
源码分析
不稳定的算法不一定会报错,所以在复杂排序算法上很容易出现偶现bug。期根本原因是算法不稳定。
a = t0[1] =40 b=t0[3]=40 和 a = t0[3] =40 b=t0[1]=40情况下返回不同的值。
快速排序原本是不稳定的排序方法,对算法本身来说返回true,false无关紧要,不论2个值是否交换位置,对排序结构没有任何问题。lua error的产生与算法的实现代码有关。
lua 5.1(5.3代码和5.1略有不同,不过算法本身并无改动)
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 */
}
对于小于4个元素的排序,根本没有使用快速排序算法。而是直接进行了比较。
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] */
}
当wile循环中如果sort_comp一直返回true那么必定会超出范围,出现报错。所以偶现error其实是特殊的序列+不稳定算法(return true)倒是的问题。
使用建议
建议:
1、使用排序规则明确,稳定的sort_comp
2、慎重使用 return true
3、不要使用return a>=b 或者 return a <=b
4、确保 a==b 返回 false