无法使用关联索引对表进行排序

mnn*_*mnn 1 sorting indexing lua associative-array lua-table

为什么我不能使用table.sort对具有关联索引的表进行排序?

RBe*_*eig 6

通常,Lua表是纯关联数组.除了作为Lua核心中使用的特定哈希表实现的副作用之外,没有"自然"顺序.这是有道理的,因为任何Lua数据类型(除了nil)的值都可以用作键和值; 但只有字符串和数字有任何合理的排序,然后只在相似类型的值之间.

例如,该表的排序顺序应该是:

unsortable = {
    answer=42,
    true="Beauty",
    [function() return 17 end] = function() return 42 end,
    [math.pi] = "pi",
    [ {} ] = {},
    12, 11, 10, 9, 8
}
Run Code Online (Sandbox Code Playgroud)

它有一个字符串键,一个布尔键,一个功能键,一个非整数键,一个表键和五个整数键.该函数应该排在字符串之前吗?你如何比较字符串和数字?表格应该放在哪里?那个表中没有出现的情况userdatathread价值怎么样?

按照惯例,由以1开头的连续整数索引的值通常用作列表.几个函数和常用习语遵循这个约定,并且table.sort是一个例子.在列表上运行的函数通常会忽略存储在不属于列表的键的任何值.再一次,table.sort是一个例子:它只对那些存储在列表中的键中的元素进行排序.

另一个例子是#运营商.对于上表,#unsortable是5因为unsortable[5] ~= nilunsortable[6] == nil.请注意,math.pi即使pi介于3和4之间,也不会计算存储在数字索引处的值,因为它不是整数.此外,也不计算其他非整数键.这意味着一个简单的for循环可以迭代整个列表:

for i in 1,#unsortable do
    print(i,unsortable[i])
end
Run Code Online (Sandbox Code Playgroud)

虽然这通常写成

for i,v in ipairs(unsortable) do
    print(i,v)
end
Run Code Online (Sandbox Code Playgroud)

简而言之,Lua表是无序的值集合,每个表都由一个键索引; 但是从1开始,顺序整数键有一个特殊的约定.

编辑:对于具有适当的部分排序的非整数键的特殊情况,有一个涉及单独索引表的解决方法.所描述的由字符串值键入的表的内容是这个技巧的合适示例.

首先,以列表的形式收集新表中的密钥.也就是说,使用从键开始的连续整数索引表,并将键作为值进行排序.然后,使用该索引以所需顺序迭代原始表.

例如,这里是foreachinorder(),它使用这种技术迭代表的所有值,按照比较函数确定的顺序为每个键/值对调用一个函数.

function foreachinorder(t, f, cmp)
    -- first extract a list of the keys from t
    local keys = {}
    for k,_ in pairs(t) do
        keys[#keys+1] = k
    end
    -- sort the keys according to the function cmp. If cmp
    -- is omitted, table.sort() defaults to the < operator
    table.sort(keys,cmp)
    -- finally, loop over the keys in sorted order, and operate
    -- on elements of t
    for _,k in ipairs(keys) do
        f(k,t[k])
    end
end
Run Code Online (Sandbox Code Playgroud)

它构造一个索引,对其进行排序table.sort(),然后循环遍历排序索引中的每个元素,并f为每个元素调用该函数.该函数f传递了键和值.排序顺序由传递给的可选比较函数确定table.sort.它被调用两个要比较的元素(t在这种情况下是表的键),true如果第一个小于第二个,则必须返回.如果省略,则table.sort使用内置<运算符.

例如,给出下表:

t1 = {
    a = 1,
    b = 2,
    c = 3,
}
Run Code Online (Sandbox Code Playgroud)

然后foreachinorder(t1,print)打印:

a    1
b    2
c    3

foreachinorder(t1,print,function(a,b) return a>b end)打印:

c    3
b    2
a    1