一个常见的困惑
开发人员想要对表中的元素进行排序时。由于一个表中的元素没有顺序,所以如果想对这些元素排序,就不得不先把键值拷贝到一个数组中,然后再对数组进行排序。
假如我们要读取一个源文件,然后构造一个表来保存每个函数的名称及其生命所在的行数,形式如下:
lines = { ["luaH_set"] = 10, ["luaH_get"] = 24, ["luaH_present"] = 48, }
现在,我们想按照字母顺序输出这些函数名。如果使用 pairs
遍历表,那么函数名会随机出现。由于这些函数名是表的键,所以我们无法直接对其进行排序。不过,如果我们把他们放到数组中,那么就可以对他们进行排序了。
首先,我们必须创建一个包含函数名的数组,然后对其排序,再最终输出结果。
a = {} for n in pairs(lines) do a[#a + 1] = n end table.sort(a) for _, n in ipairs(a) do print(n) end
有些人可能会困惑,毕竟,对于 Lua
语言来说,数组也没有顺序(毕竟他们是表)。但是我们知道如何数数!因此,当我们使用有序的索引访问数组时,就实现了有序。这正是应该总是使用 ipars
而不是 pairs
来遍历数组的原因。第一个函数通过有序的键 1
, 2
等来实现有序,然而后者使用的则是天然的随机顺序(虽然大多数情况下顺序随机也无碍,但有时可能并非我们想要的)。
现在,我们已经准备好写一个按照键的顺序来遍历表的迭代器了。
function pairsByKeys (t, f) local a = {} for n in pairs(t) do --> 创建一个包含所有键的表 a[#a + 1] = n end table.sort(a, f) --> 对列表排序 local i = 0 return function () --> 迭代函数 i = i +1 return a[i], t[a[i]] --> 返回键和值 end end
工厂函数 pairsByKeys
首先把键放到一个数组中,然后对数组进行排序,最后返回迭代函数。在每一步中,迭代器都会按照数组a
中的顺序返回原始表中的一个键值对。可选参数f
允许指定一种其他的排序方式。
使用这个上述函数,可以很容易得解决开始时提出的按照顺序遍历表的问题:
for name, line in pairsByKeys(lines) do print(name,line) end
像通常一样,所有的复杂性都被隐藏到了迭代器中。