Есть ли какой-нибудь умный способ написать объект lua, чтобы он удваивался как итератор? - PullRequest
4 голосов
/ 24 мая 2011

Допустим, у меня есть какой-то "объект", который я определил в другом месте.Может быть, он представляет собой набор элементов, но сложнее, чем простая таблица.Как бы то ни было, было бы логично перебрать его.

Таким образом, для него определен метод 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

Итакесть?

Ответы [ 2 ]

3 голосов
/ 24 мая 2011

Одно небольшое изменение в вашем примере сделает семантику намного менее болезненной:

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})

Без конструкции итератора вы будете эффективно использовать один итератор для нескольких итераций, а это значит, что вам нужно сохранить итераторукажите состояние в закрытии объекта / создания и сбросьте его после завершения, чтобы следующий вызов снова перезапустил итерацию.Если вы действительно хотите это сделать, лучшим решением будет написать что-то для конкретной реализации итерации, но при этом будет выполнена общая итерация:

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

Опять: это должно действительно быть приспособлен к вашему случаю использования.

0 голосов
/ 24 мая 2011

Объект, используемый после in, должен быть функцией, которая будет неоднократно вызываться универсальным циклом for.

Я не уверен, что вы можете сделать таблицу или пользовательский объект вызываемым какфункции, но даже в этом случае проблема заключается в том, что ваш объект может иметь только одно внутреннее состояние итератора - то есть он не разрешит несколько итераций для одного и того же объекта (ни одновременно, ни последовательно), если только вы каким-то образом не сбросите его явно.

Как ответил Стюарт, вы можете использовать метаметод __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

Но часто у нас не будет чего-то подобного, тогда она получает большесложный.


Я думал об использовании сопрограмм здесь, но они все еще нуждаются в фабричном методе (которого мы хотим избежать).Это приведет к чему-то похожему на то, что написал Стюарт (т.е. сохранит состояние итератора где-то в самом объекте или в какой-либо другой переменной, связанной с объектом), и с помощью параметра и / или результата итераторов решит, когда создавать / очищатьитератор объект / состояние.

Здесь ничего не выиграно.

...