有什么聪明的方法可以编写lua对象,使其兼作迭代器?

问题描述

| 可以说我有一些在其他地方定义的“对象”。也许它代表一组项目,但是比一个简单的表复杂。无论是什么,在其上进行迭代都是合乎逻辑的。 这样,它定义了一个“ 0”方法。所以我可以这样写:
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
那有吗     

解决方法

对您的示例进行一些小的更改将使语义上的痛苦减轻很多:
local myObject = AbstractObject:new()

for obj in myObject() do
    obj:foo()
end
这样,您可以使用元表定义
__call
元方法以返回
myObject:interator()
,并在code6ѭ中看起来像这样:
setmetatable(newobject,{__call = function() return newobject:iterator() end})
如果没有构造迭代器,您将有效地重复使用单个迭代器进行多次迭代,这意味着您需要将迭代器状态保留在对象/创建闭合中,并在完成后将其重置,以便下次调用重新开始再次迭代。如果您确实想这样做,最好的解决方案实际上是为特定的迭代实现编写一些东西,但这将执行通用迭代:
local iterator

--table.pack is planned for 5.2
local pack = table.pack or function(...)
  local t = {...}
  t.n = select(\'#\',...)
  return t
end

--in 5.1 unpack isn\'t in table
local unpack = table.unpack or unpack

function metamethods.__call(...)
  if not iterator then
    iterator = newobject:iterator()
  end

  local returns = pack(iterator(...))

  if returns[1] == nil then
    --iteration is finished: next call will restart iteration
    iterator = nil
  end
  return unpack(returns,1,returns.n)
end
再说一遍:应该根据实际情况进行调整。     ,在“ 9”之后使用的对象必须是一个函数,通用“ 10”循环将重复调用该函数。 我不确定是否可以使表或用户对象像函数一样可调用,但是即使那样,问题仍然是您的对象只能具有一个内部迭代器状态-即,它不允许在同一对象上进行多次迭代(并发或顺序),除非您以某种方式明确地重置了它。 正如Stuart回答的那样,您可以适当地使用
__call
元方法来返回迭代器,但随后您必须编写
for obj in myObject() do
    obj:foo()
end
这不是我们想要的。 在PiL中阅读更多内容,我发现在for循环中使用了更多组件:不变循环状态和控制变量的当前值,这些组件在每次调用中都传递给迭代器函数。如果我们不在
in
表达式中提供它们,它们将被初始化为
nil
。 因此,我的想法是使用这些值来区分各个调用。 如果您可以为集合创建一个
next(element)
函数,该函数为每个元素返回下一个元素,则实现将很简单:
metatable.__call = function(_state,_last)
    if(_last == nil) then
       return obj:first()
    else
       return obj:next(_last)
   end
end
但是通常我们不会有这样的东西,然后变得更加复杂。 我曾考虑过在这里使用协程,但是这些协程仍然需要一种工厂方法(我们要避免这种方法)。 这将导致类似Stuart所写的内容(即,将迭代器状态保存在对象本身或与该对象相关的其他变量中的某个位置),并使用参数和/或迭代器结果来决定何时创建/清除迭代器对象/状态。 这里什么都没有赢。