Loops 如何在Lua中对二维表生成迭代器?
我有一个由表组成的lua表,所以它是二维的:根->子->孙 此层次结构的任何级别都不能保证“类似于数组”。第一个级别有带“零间隙”的整数,第二个级别甚至没有整数索引(而是表索引) 所讨论的表是lib中的私有结构。我想为库用户提供一种解析其孙子的方法。我不太关心它们被解析的顺序,只要它们都是 我想到的第一件事是使用接受回调的函数:Loops 如何在Lua中对二维表生成迭代器?,loops,multidimensional-array,lua,iterator,Loops,Multidimensional Array,Lua,Iterator,我有一个由表组成的lua表,所以它是二维的:根->子->孙 此层次结构的任何级别都不能保证“类似于数组”。第一个级别有带“零间隙”的整数,第二个级别甚至没有整数索引(而是表索引) 所讨论的表是lib中的私有结构。我想为库用户提供一种解析其孙子的方法。我不太关心它们被解析的顺序,只要它们都是 我想到的第一件事是使用接受回调的函数: -- this scope has access to root function eachGrandChild(callback) for _,child in
-- this scope has access to root
function eachGrandChild(callback)
for _,child in pairs(root) do
for index,grandChild in pairs(child)
callback(index, grandChild)
end
end
end
用法:
-- no access to root, only to eachGrandChild
eachGrandChild(function(index, grandChild) print(index, grandChild) end)
这一点是可以理解的
我的问题是:我可以用一个类似的功能来代替吗?
我说的是一些可以让我做到这一点的事情:
for index,grandChild in iterator() do
print(index, grandChild)
end
我已经想了一段时间了,但是我没办法解决它。我看到的所有示例都使用数字来轻松地在每次迭代中“管理迭代器的状态”。因为我没有数字,所以有点卡住了。使编写这种迭代器变得容易。协同程序是一个函数,其执行可以暂停和恢复,概念上类似于线程。一个协同程序可以包含深度嵌套的循环,从最内部的循环中产生一个值,然后在恢复时从停止的地方继续。当它产生时,恢复它的调用方可以接收产生的值
在您的例子中,将eachGrandChild
转换为生成孙子的生成器函数
function eachGrandChild(root)
for _,child in pairs(root) do
for index,grandChild in pairs(child) do
coroutine.yield(index, grandChild)
end
end
end
然后使用coroutine.wrap
创建一个函数,该函数将为生成器创建一个coroutine,并在每次调用该函数时恢复该函数
function grandChildren(t)
return coroutine.wrap(function() eachGrandChild(t) end)
end
现在您有了迭代器:
for key, val in grandChildren(root) do
print(key, val)
end
我同意Mud的观点,即协同程序是解决问题的最佳方法 为了便于比较,我编写了一个没有协同例程的迭代器 为每个元素调用第一个函数
eachGrandChild
。它使用一个state
变量,包含两个索引(顶层和二级)
迭代器由helper函数初始化:
function grandChildren(root)
return eachGrandChild, {childIndex = next(root)}
end
现在迭代器可以正常使用了:
for key, val in grandChildren(root) do
print(key, val)
end
与基于协同程序的版本相比,
eachGrandChild
有更多的代码行,更难阅读。。我说的“管理函数状态”部分实际上是在对我喊“协同程序”,但我没有把这些点联系起来。我有点担心执行速度(每帧将执行多次解析循环,因此速度必须相当快),但我会做一些测试。@kikito:您的直接回调方法肯定会更有效,部分原因是减少了函数调用开销(恢复、产量)。但这并不像你想象的那么糟糕(Lua的合作项目是)。例如,如果您对协程实现与prapin的实现进行基准测试,我怀疑您会发现协程更快。您在所有方面都是正确的。即使每次创建新的匿名函数时,回调代码也是最快的。协同程序完成同样的工作需要大约200%的时间,而基于下一个的解决方案需要大约220%的时间。所以我想我会坚持回拨。尽管如此,这还是一次很好的学习经历!:)只要记住。是的,200%听起来很糟糕,但我们谈论的是几微秒之间的差异。你是说每帧有数千次这样称呼吗?“过早的邪恶是所有优化的根源”:我知道这句话。是的,每帧将调用数千次-它将位于冲突库的中心(root
实际上是一个空间索引),谢谢您的回答。尽管如此,我还是会尝试这两种方法——你的方法可能更有效,如果稍微长一点的话。如果可能的话,我会声明这两个答案都是正确的。你肯定应该得到一个+1,我已经冒昧地对迭代器代码进行了一点重构——名称而不是幻数,一段时间而不是重复直到,以及一个简化的“孙辈用完”条件。我还从状态中删除了root
,因为假定它在范围中可用。我希望你不介意我编辑你的答案。@kikito谢谢你编辑代码。这一点现在更具可读性。
for key, val in grandChildren(root) do
print(key, val)
end