zoukankan      html  css  js  c++  java
  • Lua table.sort()原理和使用的坑




    参考博客:lahmiley

    最近使用table.sort()的时候遇到了一个报错的问题:invalid order function for sorting。
    感觉很奇怪,于是总结下方法的原理和报错的原因。

    先讨论下lua里面sort的实现:

    table.sort原理和内部实现

    • table.sort的内部使用的是快排,并对其做了三点优化。

    • 刷题的时候可能我们写的快排大部分会直接使用数组开头作为基点,但是这样的话,当我们遇到数组已经是排好序的情况的时候,快排会退化为冒泡,时间复杂度升到了On^2,会很耗费性能。所以这里对其进行了优化,使用数组的开头、中间、结尾中间大的元素作为基点,减少特殊情况时快排的次数。

    • 对于迭代函数中的分割数组长度小于等于3的,直接通过比较大小交换位置,形成有序数组,这样做的目的是减少递归调用的深度。

    • 每次通过锚点把分割数组分成两半之后,对长度较小的一半进行递归调用,另一半则继续通过While继续分割处理,目的应该也是减少递归调用的深度

    table.sort源码

        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);
            
                //上面代码是对分割数组长度小于等于3的进行比较和排序
                //并从数组初始点、中间点、结尾点中选择中位数作为锚点
            
                /* 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 */
            
            //上面代码是让分割后长度较短的数组继续迭代,长度较长的则继续通过while进行快排算法,减少递归调用的次数
        }
    

    报错的位置源码:

        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] */
        }
    

    报错原因和解决

    报错出现的条件:当基点和数组边界值有相等时,这时如果排序方法sort_comp返回true,则会造成数组越界。

    local array = {9,15,9,222,10}
    
    --此时的基点是9,和开头元素相等
    table.sort(array, function(a, b)
        --递增
        return a <= b
    end)
    --报错:invalid order function for sorting
    

    报错解决:当比较的两个值相等时,返回false即可。

    local array = {9,15,9,222,10}
    
    table.sort(array, function(a, b)
        return a < b
    end)
    

    补充(当数组中有为nil的元素时)

    sort排序的表必须是从1-n连续的,不能有nil。
    不然的话,排序会把nil的前一个元素当做尾元素来进行排序。

    local array = {9,15,9,nil,10}
    
    table.sort(array, function(a, b)
        return a < b
    end)
    
    for i = 1, #array do
        print(array[i])
    end
    --[[ 输出:
        9 9 15
    ]]
    
  • 相关阅读:
    常用模块(一)
    面向对象进阶:反射以及内置方法
    面向对象三大特性之多态、封装与装饰器
    面向对象的三大特性之继承
    python之面向对象
    python之内置函数
    python之迭代器,生成器以及列表推导式
    比较好用的linux命令
    使用redission实现分布式信号量以及遇到的一些坑
    linux一些命令
  • 原文地址:https://www.cnblogs.com/Fflyqaq/p/13813762.html
Copyright © 2011-2022 走看看