Lua - 将协程递归重写为尾调用递归

我需要编写一个可以遍历嵌套表格的迭代器。我使用 coroutine 编写了一个迭代器。

它创建了一个包含路径和值的数组,比如 {{key1, key2, key3}, value} 表示要获取 value,需要这样做 nested_table[key1][key2][key3]

然后我轻松地编写了 find(), findall(), in() 函数,生活如此美好。

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

然后我发现我要使用的 Lua 环境已将 coroutine 加入黑名单,我们不能使用它。因此,我尝试在没有 coroutine 的情况下获得相同的功能。

-- 测试数据

local pool = {}
test = {
  ['a'] = 1,
  ['b'] = {
    ['c'] = {2, 3},
    ['d'] = 'e'
  }
}

-- 树遍历

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 -- 这是 'yield' 部分
  if type(value) == 'table' then
    for k,v in pairs(value) do
      flatten(v, table.extend(path, k))
    end
  end
end

-- 测试遍历函数

flatten(test)

for k, v in pairs(pool) do
  if type(v) == 'table' then v = '[table]' end
  print(table.concat(k, ' / ')..' -> '..v)
end

这段代码返回了我所需要的东西:

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]

但是我仍然有一个问题:我无法使用全局变量 pool,因为这段代码是并行调用的。并且我无法在 for 循环中做适当的尾调用递归(return flatten(...)),因为它只会返回一次。

所以我的问题是:我如何将这个函数打包成可以并行调用的东西?或者换句话说:我是否可以通过返回值实现 'yield' 部分的功能,而不是将结果传递到全局变量中?

我尝试将它制作成一个对象,遵循这里的模式,但我无法让它工作。

点赞
用户1847592
用户1847592

你可以将pool变量设为局部变量:

test = {
   ['a'] = 1,
   ['b'] = {
      ['c'] = {2, 3},
      ['d'] = 'e'
   }
}

-- 树遍历

function table.extend(tbl, element)
   local copy = {table.unpack(tbl)}
   table.insert(copy, element)
   return copy
end

local function flatten(value, path, pool)    -- 第三个参数是pool
   path = path or {'root'}
   pool = pool or {}                                    -- 初始化pool
   pool[path] = value
   if type(value) == 'table' then
      for k,v in pairs(value) do
         flatten(v, table.extend(path, k), pool)  -- 在递归中使用pool
      end
   end
   return pool                           -- 将pool作为函数结果返回
end

-- 测试遍历函数

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
2017-06-28 09:10:41