我正在处理大量用 Lua 编写的数据文件。大部分都是这样写的,以“电话本”为例:
data = {
-- First Level - country
USA = {
-- Second level - city
Denver = {
-- Third level - actual entries
{name = 'John', number = '12345'},
-- more entries
},
Washington = {
{name = 'Ann', number = '54321'},
-- more entries
},
-- more cities with entries
},
-- more countries with cities and entries
}
因此,第一级是“国家/地区”,第二级是“城市”的事实是隐含的,但它使数据更加紧凑。
现在,当实际搜索某些数据时,我想将此数据作为条目进行迭代包括这种分级的隐式信息。
-- Coroutine yielding entries including level data
function corIter(data)
for country,l1 in pairs(data) do
for city,l2 in pairs(l1) do
for _,entry in pairs(l2) do
-- Copy the entry
local out = {}
for k,v in pairs(entry) do
out[k] = v
end
-- Add level properties
out.country = country
out.city = city
coroutine.yield(out)
end
end
end
end
-- Iterate over the entries
local cor = coroutine.create(corIter)
local _, entry = coroutine.resume(cor, data)
while entry do
-- Handle the entry, has 'name', 'number', 'country' and 'city' keys
table.print(entry) -- (custom print function I use)
-- Get the next one
_, entry = coroutine.resume(cor)
end
但我认为这种方法可能很糟糕,因为它使整个线程保持活动状态,只是为了以特定的方式迭代该死的表。
还有其他“明显”的解决方案吗?关键是性能和易用性。我并不完全需要一个通用的解决方案(对于数据表内任意数量的“级别”),但这一切都像黑客一样。
local function phones(d)
local cn, c, tn, t, i
return
function()
local a
repeat
if tn then
a, i = t[i], i+1
if not a then
i, tn, t = 1, next(c, tn)
end
else
cn, c = next(d, cn)
i, tn, t = 1, next(c or {})
end
until a or not cn
return cn, tn, a
end
end
for country, town, abonent in phones(data) do
print(country, town, abonent.name, abonent.number)
end
您可以在 Lua 中创建自己的自定义迭代器,无需使用协程。迭代器是调用时返回结构中的下一个元素的函数(您可以使用任何您想要的结构)。
您的示例的迭代器将是这样的:
function corIter(data)
local itOut = {}
for country,l1 in pairs(data) do
for city,l2 in pairs(l1) do
for _,entry in pairs(l2) do
-- Copy the entry
local out = {}
for k,v in pairs(entry) do
out[k] = v
end
out.country = country
out.city = city
table.insert(itOut,out)
end
end
end
local i = 0
return function()
i = i + 1
return itOut[i]
end
end
“corIter”返回的匿名函数将返回数据中的下一个元素。请注意,当我们使用“对”将条目复制到另一个表以迭代它们时,没有任何东西可以保证条目的顺序将保持为原始顺序。
现在您可以使用此代码来打印条目:
for entry in corIter(data) do
print(entry) -- this is a table
for k,v in pairs(entry) do
print(k,v) -- these are the actual values
end
end
@Egor 的答案非常好,因为它定义了 a lua iterator 而不是将数据收集到表中。迭代器有几个好处:
但是,我发现它太简洁了,无法理解。我对其进行了重新设计,以使用更具描述性的名称和更少的复合语句。
local function phones(phonebook)
local country_name, country_t, city_name, city_t, i
return function()
local client_t
repeat
if city_name then
client_t = city_t[i]
i = i+1
if not client_t then
i = 1
city_name, city_t = next(country_t, city_name)
end
else
country_name, country_t = next(phonebook, country_name)
i = 1
city_name, city_t = next(country_t or {})
end
until client_t or not country_name
return country_name, city_name, client_t
end
end
local data = {
USA = {
Denver = {
{name = 'John', number = '12345'},
{name = 'Johaan', number = '13336'},
},
Washington = {
{name = 'Ann', number = '54321'},
{name = 'Ana', number = '53132'},
},
},
}
for country, town, client in phones(data) do
print(country, town, client.name, client.number)
end
输出:
USA Washington Ann 54321
USA Washington Ana 53132
USA Denver John 12345
USA Denver Johaan 13336