4

他の場所で定義した「オブジェクト」があるとしましょう。アイテムのセットを表しているかもしれませんが、単純なテーブルよりも複雑です。それが何であれ、それを反復することは論理的です。

そのため、iteratorメソッドが定義されています。だから私はこれを書くことができます:

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

それで、ありますか?

4

2 に答える 2

3

例を少し変更すると、セマンティクスの痛みが大幅に軽減されます。

local myObject = AbstractObject:new()

for obj in myObject() do
    obj:foo()
end

__callそうすれば、メタテーブルを使用して、を返すメタメソッドを定義できます。myObject:interator()コードは で次のようになりAbstractObject:new()ます。

setmetatable(newobject, {__call = function() return newobject:iterator() end})

イテレータの構築がなければ、複数のイテレーションで 1 つのイテレータを効果的に再利用することになります。つまり、オブジェクト/作成クロージャでイテレータの状態を保持し、終了後にリセットして、次の呼び出しでイテレーションを再開する必要があります。また。本当にこれを行いたい場合、最善の解決策は実際には特定の反復実装用に何かを記述することですが、これは一般的な反復を実行します。

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

繰り返しますが、これはユースケースに合わせて調整する必要があります。

于 2011-05-24T16:25:29.023 に答える
0

後に使用されるオブジェクトは、汎用ループinによって繰り返し呼び出される関数でなければなりません。for

テーブルまたはユーザー オブジェクトを関数のように呼び出し可能にすることができるかどうかはわかりませんが、それでも問題は、オブジェクトが 1 つの内部反復子状態しか持てないことです。つまり、同じオブジェクトに対して複数の反復を許可しません (どちらも何らかの方法で明示的にリセットしない限り。

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 が書いたものと似たような結果になり (つまり、イテレータの状態をオブジェクト自体のどこかに保存するか、オブジェクトに関連する他の変数に保存します)、パラメータおよび/またはイテレータの結果を使用して、いつオブジェクトを作成/消去するかを決定します。イテレータ オブジェクト/状態。

ここでは何も勝ちませんでした。

于 2011-05-24T16:24:32.673 に答える