Reputation: 960
I have to write an iterator that can traverse nested tables. I wrote one using coroutine
.
It created an array of (path, value) pairs, e.g. {{key1, key2, key3}, value}
meaning that to get value
you got to do nested_table[key1][key2][key3]
.
Upon that I wrote find()
, findall()
, in()
with ease, life was bright.
function table.extend(tbl, new_value)
local tbl = {table.unpack(tbl)}
table.insert(tbl, new_value)
return tbl
end
function iterate(tbl, parent)
local parent = parent or {}
if (type(tbl)=="table") then
for key, value in pairs(tbl) do
iterate(value, table.extend(parent, key))
end
end
coroutine.yield(parent, tbl)
end
function traverse(root)
return coroutine.wrap(iterate), root
end
Then I realized that the Lua environment I have to work with has coroutine
blacklisted. We can not use that. So I try to get the same functionality without coroutine
.
-- testdata
local pool = {}
test = {
['a'] = 1,
['b'] = {
['c'] = {2, 3},
['d'] = 'e'
}
}
-- tree traversal
function table.extend(tbl, element)
local copy = {table.unpack(tbl)}
table.insert(copy, element)
return copy
end
local function flatten(value, path)
path = path or {'root'}
pool[path] = value -- this is the 'yield'
if type(value) == 'table' then
for k,v in pairs(value) do
flatten(v, table.extend(path, k))
end
end
end
-- testing the traversal function
flatten(test)
for k, v in pairs(pool) do
if type(v) == 'table' then v = '[table]' end
print(table.concat(k, ' / ')..' -> '..v)
end
This code returns what I need:
root -> [table]
root / b / c / 1 -> 2
root / b -> [table]
root / a -> 1
root / b / d -> e
root / b / c / 2 -> 3
root / b / c -> [table]
But I still have a problem: I can not use a global variable, pool
, this code is called paralleled. And I can not do proper tail-call recursion (return flatten(...)
) from a for
cycle as it would return only once.
So my question: how do I package this function into something that can be called in parallel? Or in other words: can I achieve what the 'yield' part does with a return value, instead of piping the results into a global variable?
I tried to make it an object, following the patterns here, but I could not get it work.
Upvotes: 3
Views: 443
Reputation: 23727
You can make pool
variable local:
test = {
['a'] = 1,
['b'] = {
['c'] = {2, 3},
['d'] = 'e'
}
}
-- tree traversal
function table.extend(tbl, element)
local copy = {table.unpack(tbl)}
table.insert(copy, element)
return copy
end
local function flatten(value, path, pool) -- third argument is the pool
path = path or {'root'}
pool = pool or {} -- initialize pool
pool[path] = value
if type(value) == 'table' then
for k,v in pairs(value) do
flatten(v, table.extend(path, k), pool) -- use pool in recursion
end
end
return pool -- return pool as function result
end
-- testing the traversal function
local pool = flatten(test)
for k, v in pairs(pool) do
if type(v) == 'table' then v = '[table]' end
print(table.concat(k, ' / ')..' -> '..v)
end
Upvotes: 6