Почему это не допустимый итератор таблицы?
Я видел этот пример в уроке lua:
function fromto(a, b)
return
function(state, seed)
if (seed >= state) then return nil
else return seed+1 end
end, b, a-1
end
Он возвращает следующие целочисленные значения от a до b включительно. Поэтому я попытался применить ту же логику, написав следующий итератор таблицы:
function values(t) -- t is a table
return
function(state, seed)
return state[seed+1]
end, t, 0
end
Он возвращает первое значение правильно, но затем выдает ошибку, говоря, что я не могу делать арифметику в строковом значении (seed). Но разве seed не получает значение 0, которое является целым числом? Что происходит?
Поскольку нет процесса инкрементирования на первом например (например, a = a + 1), я думаю, что Луа может справиться с этим за кулисами... но если это не так, возможно, это и есть причина моего замешательства.
2 ответа:
seedэто плохое имя для второго параметра вашей итерационной функции. На самом деле это управляющая переменная. Вы "затравляете" его, когда возвращаете 0 изvalues, но после этого его значение изменяется во время цикла: оно принимает первое значение, возвращенное из функции итератора.Вы вернули строку из итератора (
return state[seed+1]), поэтому при следующем вызове функции итератора эта строка была передана. Вы пытались сделать математику на нем, и... БАМ.Универсальный
forпринимает три параметра: функцию итератора, инвариантное состояние и начальное управляющее значение. Итератор вызывается с состоянием и значением элемента управления. Затем итератор возвращает следующее управляющее значение илиnil, чтобы указать, что итерация выполнена.Таким образом, первый вызовt = {"foo","bar","zip","zap"} local function iteratorFunction (state, index) index = index + 1 local val = state[index] if val == nil then return nil end return index, val end for k, v in iteratorFunction, t, 0 do print(k,v) enditeratorFunctionполучаетtи0в качестве параметров. следующий вызовiteratorFunctionполучаетtи первое значение, возвращенное изiteratorFunction, ну и так далее и так далее.Когда вы пишете функцию "генератор", такую как
values, Вы просто возвращаете три начальных значения, требуемые циклом generic for, чтобы ваш код был более лаконичным при использовании этого итератора:function values(t) local function iteratorFunction (state, index) index = index + 1 local val = state[index] if val == nil then return nil end return index, val end return iteratorFunction, t, 0 -- the same three values used in the for loop above end for k, v in values(t) do print(k,v) endЕдинственным обязательным параметром для generic
forявляется функция итератора. Инвариантное состояние и управляющая переменная могут быть равны нулю, что можно сделать, если ваша итерационная логика выполняется в закрытии:function values(t) local index = 0 -- our iterator function is a closure bound to `index` and `t` local function iteratorFunction() index = index + 1 local val = t[index] if val == nil then return nil end return index, val end return iteratorFunction, t, 0 -- the same three values used in the for loop above end for k, v in values(t) do print(k,v) endЕсли вы измените
return index, valна простоreturn val,values(t)будет сейчас перебирайте только значения вt. Мы не могли сделать этого раньше, потому что нам нужно было вернуть управляющую переменную для следующей итерации цикла. При закрытии мы поддерживаем управляющую переменную через переменную, связанную с закрытием (она же "upvalue").
Можно использовать другую форму итераторов с замыканиями:
local function values(t) local i = 0 return function() i = i + 1 return t[i] end end for x in values({1, 2, 3}) do print(x) end1 2 3
Итератор продолжается до тех пор, пока не будет возвращен
nil. Недопустимое поле таблицы (за последним) всегда равно нулю, поэтому вам просто нужно продолжать идти.Форма с передаваемыми значениями счетчика в вашем примере использует возвращаемое значение для обновления последнего значения счетчика и обычно не используется. Вы обновляете
seedс вашей записью таблицы, которая является строкой, поэтому следующий проход должен завершиться ошибкой.Кстати.: твой значения функция делает то же самое, что и
ipairs.