遍历 Lua 中对象的值(按顺序)

huangapple go评论68阅读模式
英文:

Iterate over values in an object in Lua (by order)

问题

当我调用 p.main() 时,我希望它打印出 Jan Feb ... Dec(按顺序),但实际上它打印出的是 Jan Mar Apr Oct Feb Dec Nov Aug Jul May Jun Sep,对我来说看起来是相当随机的顺序。

提前感谢!

英文:

I am looking for a simple solution for my problem. I've got this code:

local Month = {
	Jan = 1, Feb = 2, Mar = 3, Apr = 4,
	May = 5, Jun = 6, Jul = 7, Aug = 8,
	Sep = 9, Oct = 10, Nov = 11, Dec = 12
}
...
p.main = function()
	local text = ""
	for k,v in pairs(Month) do
		text = text .. " " .. k;
	end
	return text;
end

When I call p.main() I would like it to print Jan Feb ... Dec (in order) though it prints Jan Mar Apr Oct Feb Dec Nov Aug Jul May Jun Sep which seems to me quite a random order.

Thanks in advance!

答案1

得分: 6

Lua表中的哈希部分是无序的。字符串键存储在哈希部分。这就是为什么您观察到"随机"顺序的原因。

只需将您的Month表从月份名称到数字的映射转换为数字到名称的映射,即反转它并将其转换为列表:

local monthnames = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}

然后,您可以使用ipairs(而不是pairs)方便地按顺序迭代它 - 更重要的是,您的p.main函数简化为对table.concat的单个调用,这是Lua的内置函数,用于连接字符串列表,并且由于该值是常量,您可以预先计算它:

local space_separated_monthnames = table.concat(monthnames, " ")
function p.main()
    return space_separated_monthnames
end

这不会添加前导空格,但我认为这并不是您的意图。

如果您需要保留旧的查找,也没问题 - 您可以从列表生成它:

local monthnumber = {}
for i, name in ipairs(monthnames) do
    monthnumber[name] = i
end

还要注意,在循环中进行字符串连接是Lua中的一个反模式,因为它既低效(二次 vs. 线性,如果您至少构建较长的字符串,这会非常明显),又不方便编写(例如,您的代码生成一个可能不需要的前导空格,而且需要更多的代码来避免它)。一般来说,如果您看到类似以下的东西:

local str = vals[1]
for i = 2, #vals do
    str = str .. separator .. vals[i]
end

请对其进行重构:

local str = table.concat(vals, separator)

这既有助于性能又有助于可读性。

英文:

The hash part of tables in Lua is unordered. String keys go in the hash part. This is why you're observing the "random" order.

Simply convert your Month table from a mapping from month names to numbers to a mapping from numbers to names, i.e. flip it and convert it to a list:

local monthnames = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}

then you can conveniently iterate it in order using ipairs (rather than pairs) - even more, your p.main function simplifies to a single call to table.concat, a Lua built-in function for concatenating a list of strings, and since the value is constant, you can pre-calculate it:

local space_separated_monthnames = table.concat(monthnames, " ")
function p.main()
    return space_separated_monthnames
end

This won't add a leading space, but I don't think that's intended anyways.

If you need to keep the old lookup, no problem - you can generate it from the list:

local monthnumber = {}
for i, name in ipairs(monthnames) do
    monthnumber[name] = i
end

Also note that string concatenation in a loop is an antipattern in Lua since it is both inefficient (quadratic vs. linear, very noticeable if you're building longer strings at least) and inconvenient to write (e.g. your code emits a probably unwanted leading space and more code would be required to not emit it). In general, if you see something like:

local str = vals[1]
for i = 2, #vals do
    str = str .. separator .. vals[i]
end

refactor it to

local str = table.concat(vals, separator)

both for performance and readability.

huangapple
  • 本文由 发表于 2023年5月21日 23:58:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76300762.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定