按值的顺序迭代表格

假设我有这样一个表格:

{
   value = 4
},
{
   value = 3
},
{
   value = 1
},
{
   value = 2
}

我想要遍历它并按顺序打印值,输出应该像这样:

1
2
3
4

我该怎么做?我知道如何使用ipairspairs,以及table.sort,但这仅适用于使用table.insert并且键有效的情况,我需要按值的顺序循环遍历它。

我尝试了自定义函数,但它只是按照错误的顺序打印它们。

我尝试过:

  • 创建一个索引并循环该索引
  • 对表格进行排序(抛出错误:尝试在表格和表格上执行__lt
  • 以及排序、索引和其他表格的组合,这不仅没有起作用,而且还使它变得非常复杂。

我真的被难住了。

点赞
用户501459
用户501459

排序表格

这是正确的解决方案。

(报错:尝试在表格和表格上执行 __lt)

听起来你尝试使用 a < b

为了使 Lua 能够对值进行排序,它必须知道如何进行比较。它知道如何比较数字和字符串,但是默认情况下它不知道如何比较两个表格。考虑这个例子:

local people = {
    { name = 'fred', age = 43 },
    { name = 'ted', age = 31 },
    { name = 'ned', age = 12 },
}

如果我在 people 上调用 sort,Lua 怎么知道我的意图呢?它不知道 'age' 或 'name' 是什么意思,也不知道我想要使用哪个进行比较。我不得不告诉它。

可以向表格添加 _metatable_,告诉 Lua 对于表格来说 < 操作符的含义是什么,但您还可以向 sort 提供回调函数,告诉它如何比较两个对象。

您向 sort 提供一个接收两个值的函数,然后使用您对表格的知识返回第一个值是否“小于”第二个值。在您的表格的情况下:

table.sort(t, function(a,b) return a.value < b.value end)

for i,entry in ipairs(t) do
    print(i,entry.value)
end
2015-08-11 15:27:10
用户5240636
用户5240636

如果您想保留原始表格不变,可以创建一个自定义的“按值排序”的迭代器,如下所示:

local function valueSort(a,b)
    return a.value < b.value;
end

function sortByValue( tbl ) -- 作为迭代器使用
    -- 构建新表格进行排序
    local sorted = {};
    for i,v in ipairs( tbl ) do sorted[i] = v end;
    -- 对新表格进行排序
    table.sort( sorted, valueSort );
    -- 返回迭代器
    return ipairs( sorted );
end

当调用 sortByValue() 时,它克隆了 tbl 到一个新的 sorted 表格,接着对 sorted 表进行排序。然后将 sorted 表格交给 ipairs()ipairs() 输出迭代器供 for 循环使用。

使用方法:

for i,v in sortByValue( myTable ) do
  print(v)
end

虽然这确保了原始表格保持不变,但它的缺点是每次进行迭代时迭代器都必须克隆 myTable 来制作新的 sorted 表格,然后对该 sorted 表进行 table.sort 操作。

如果性能至关重要,可以通过“缓存” sortByValue() 迭代器所做的工作来极大地加快速度。更新代码如下:

local resort, sorted = true;

local function valueSort(a,b)
    return a.value < b.value;
end

function sortByValue( tbl ) -- 作为迭代器使用
    if not sorted then -- 重建sorted表格
        sorted = {};
        for i,v in ipairs( tbl ) do sorted[i] = v end;
        resort = true;
    end
    if resort then -- 对“sorted”表格进行排序
        table.sort( sorted, valueSort );
        resort = false;
    end
    -- 返回迭代器
    return ipairs( sorted );
end

每次向 myTable 添加或删除一个元素时,将 sorted = nil。这会让迭代器知道它需要重建 sorted 表格(还需要重新排序)。

每次在内嵌表格中更新 value 属性时,将 resort = true。这会让迭代器知道它必须进行 table.sort 操作。

现在,当使用迭代器时,它将尝试重用缓存的 sorted 表格之前排序的结果。

如果找不到 sorted 表格(例如:在迭代器的第一次使用或因为将 sorted = nil 设置为空来强制重建),它将重建它。如果它看到需要重新排序(例如:在第一次使用迭代器时仍然需要重新排序,或者如果已经重建了 sorted 表,或者设置了 resort = true),则它将对 sorted 表格进行重新排序。

2015-08-23 14:38:05