有没有一些聪明的方法来编写 lua 对象,使其兼作迭代器?
假设我有一些在其他地方定义的“对象”。也许它代表一组项目,但比简单的表格更复杂。无论它是什么,迭代它都是合乎逻辑的。
因此,它定义了一个迭代器方法。所以我可以这样写:
local myObject = AbstractObject:new()
for obj in myObject:iterator() do
obj:foo()
end
我想知道是否有一些我可以做的元方法技巧,这将允许我这样写:
local myObject = AbstractObject:new()
for obj in myObject do
obj:foo()
end
那么有吗?
Lets say I have some "object" that I've defined elsewhere. Maybe it represents a set of items, but is more complex than a simple table. Whatever it may be, it would be logical to iterate over it.
As such, it has a iterator
method defined. So I can write this:
local myObject = AbstractObject:new()
for obj in myObject:iterator() do
obj:foo()
end
What I'm wondering is if there is some metamethod trickery that I can do, which will allow me to write this:
local myObject = AbstractObject:new()
for obj in myObject do
obj:foo()
end
So is there?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对示例进行一个细微的更改将使语义变得不那么痛苦:
这样,您可以使用元表来定义
__call
元方法来返回myObject:interator()
,使用类似于AbstractObject:new()
中的代码:如果没有迭代器构造,您将有效地重用单个迭代器进行多次迭代,这意味着您需要保持迭代器状态在对象/创建闭包中,并在完成后重置它,以便下一次调用将再次重新启动迭代。如果您确实想这样做,最好的解决方案实际上是为特定迭代实现编写一些内容,但这将执行通用迭代:
再次:这应该确实进行调整以适合您的用例。
One slight change to your example would make the semantics a lot less painful:
That way, you can use a metatable to define the
__call
metamethod to returnmyObject:interator()
, with code that looks something like this inAbstractObject:new()
:Without the iterator construction, you'll be effectively reusing a single iterator for multiple iterations, which means you'll need to keep the iterator state in the object/creation closure, and reset it after it finishes so the next call will restart the iteration again. If you really want to do this, the best solution would really be to write something for the specific iteration implementation, but this would perform the generic iteration:
Again: This should really be adjusted to fit your use case.
in
之后使用的对象必须是一个函数,该函数将被通用for
循环重复调用。我不确定您是否可以使表或用户对象像函数一样可调用,但即使如此,问题仍然是您的对象只能有一个内部迭代器状态 - 即它不允许对同一对象进行多次迭代(也不允许同时或顺序),除非您以某种方式显式重置它。
正如斯图尔特所回答的,您可以适当地使用 __call 元方法来返回迭代器,但是您必须编写
这并不完全是我们想要的。
在 PiL 中阅读更多内容,我发现 for 循环中使用了更多组件:不变的循环状态和控制变量的当前值,在每次调用中传递给迭代器函数。如果我们不在
in
表达式中提供它们,它们将被初始化为nil
。因此,我的想法是使用这些值来区分各个调用。
如果您可以为您的集合创建一个
next(element)
函数,该函数为每个元素返回下一个元素,那么实现会很简单:但通常我们不会有这样的东西,然后它会变得更加复杂。
我考虑过在这里使用协程,但它们仍然需要工厂方法(我们希望避免)。
它将导致类似于 Stuart 所写的内容(即将迭代器状态保存在对象本身的某个位置或与对象相关的其他变量中),并使用参数和/或迭代器结果来决定何时创建/清理迭代器迭代器对象/状态。
这里没有任何胜利。
The object used after
in
must be a function, which will be called repeatedly by the genericfor
loop.I'm not sure if you can make a table or user object callable like a function, but even then the problem would be that your object can only have one internal iterator state - i.e. it would not allow multiple iterations over the same object (neither concurrently nor sequentially), unless you are somehow explicitly resetting it.
As answered by Stuart, you could use the
__call
metamethod suitably to return the iterator, but then you would have to writeThis is not quite what we want.
Reading a bit more in PiL, I see that there are more components used in the for loop: the invariant loop state, and the current value of the control variable, which are passed to the iterator function in each call. If we don't provide them in the
in
expression, they are initialized tonil
.Thus, my idea would be to use these values to distinguish the individual calls.
If you can create a
next(element)
function for your collection which returns for each element the next one, the implementation would be simple:But often we would not have something like this, then it gets more complicated.
I thought about using coroutines here, but these still need a factory method (which we want to avoid).
It would result in something similar like what Stuart wrote (i.e. saving the iterator state somewhere in the object itself or in some other variable related to the object), and using the parameter and/or the iterators result to decide when to create/clean the iterator object/state.
Nothing won here.