... до первого элемента nil.
Итератор — это объект, который можно использовать для обхода некоторых или всех элементов в контейнере стандартной библиотеки шаблонов. Каждый объект-итератор представляет определённый адрес в контейнере.
В Lua итератор — это структура, поддерживающая типы указателей. Он может перебирать каждый элемент коллекции.
Итератор обобщённого for
Обобщённый for внутренне хранит функцию-итератор. Фактически он хранит три значения: функцию-итератор, константу состояния и управляющую переменную.
Итератор обобщённого for предоставляет пары ключ/значение коллекции. Синтаксис следующий:
for k, v in pairs(t) do
print(k, v)
end
В приведённом выше коде k, v — это список переменных; pairs(t) — это список выражений.
Посмотрите на следующий пример:
Пример
for key,value in ipairs(array)
do
print(key, value)
end
Результат выполнения приведённого выше кода:
1 Google 2 Example
В приведённом выше примере мы использовали функцию-итератор ipairs, предоставляемую Lua по умолчанию.
Теперь рассмотрим процесс выполнения обобщённого for:
- Во-первых, инициализация: вычислить выражение после `in`. Выражение должно вернуть три значения, необходимые обобщённому for: функцию-итератор, константу состояния и управляющую переменную. Как и при множественном присваивании, если выражение возвращает меньше трёх значений, отсутствующие автоматически заполняются nil, а лишние игнорируются.
- Во-вторых, вызвать функцию-итератор с константой состояния и управляющей переменной в качестве аргументов (примечание: для конструкции for константа состояния бесполезна; она только получается при инициализации и передаётся функции-итератору).
- В-третьих, присвоить значения, возвращённые функцией-итератором, списку переменных.
- В-четвёртых, если первое возвращённое значение равно nil, цикл завершается; в противном случае выполняется тело цикла.
- В-пятых, вернитесь ко второму шагу и снова вызовите функцию итератора.
В Lua мы часто используем функции для описания итераторов. Каждый раз при вызове функция возвращает следующий элемент коллекции. Итераторы в Lua включают следующие два типа:
- Итераторы без состояния
- Многосостоятельные итераторы
Итераторы без состояния
Итератор без состояния — это итератор, который не сохраняет никакого состояния. Поэтому в циклах мы можем использовать итераторы без состояния, чтобы избежать дополнительных затрат на создание замыканий.
На каждой итерации функция итератора вызывается со значениями двух переменных (константы состояния и управляющей переменной) в качестве аргументов. Итератор без состояния может получить следующий элемент, используя только эти два значения.
Типичным и простым примером такого итератора без состояния является ipairs, который перебирает каждый элемент массива, индексы которого должны быть числовыми.
В следующем примере используется простая функция для реализации итератора, вычисляющего квадрат числа n:
Пример
if currentNumber<iteratorMaxCount
затем
currentNumber = currentNumber+1
return currentNumber, currentNumber*currentNumber
end
end
for i,n in square,3,0
do
print(i,n)
end
Вывод приведенного выше примера:
1 1 2 4 3 9
Состояние итерации включает таблицу, по которой выполняется обход (константа состояния, которая не меняется во время цикла), и текущий индекс (управляющая переменная). ipairs и функция-итератор очень просты. В Lua мы можем реализовать это так:
Пример
i = i + 1
local v = a[i]
if v then
return i, v
end
end
function ipairs (a)
return iter, a, 0
end
Когда Lua вызывает ipairs(a) для запуска цикла, Lua получает три значения: функцию-итератор iter, константу состояния a и начальное значение управляющей переменной 0. Затем Lua вызывает iter(a,0) и возвращает 1, a
Многосостоятельные итераторы
Во многих случаях итераторам нужно хранить несколько фрагментов информации о состоянии, а не просто константу состояния и управляющую переменную. Простейший способ — использовать замыкания. Другой способ — инкапсулировать всю информацию о состоянии в таблицу и использовать эту таблицу как константу состояния итератора. В этом случае, поскольку всю информацию можно хранить в таблице, функция-итератор обычно не нуждается во втором параметре.
В следующем примере мы создали собственный итератор:
Пример
function elementIterator (collection)
local index = 0
local count = #collection
-- функция замыкания
return function ()
index = index + 1
if index <= count
затем
-- возвращает текущий элемент итератора
return collection[index]
end
end
end
for element in elementIterator(array)
do
print(element)
end
Вывод приведенного выше примера:
Google Example
В приведённом выше примере мы видим, что elementIterator использует функцию замыкания для вычисления размера коллекции и вывода каждого элемента.
Другие расширения