... до первого элемента nil.

Итератор — это объект, который можно использовать для обхода некоторых или всех элементов в контейнере стандартной библиотеки шаблонов. Каждый объект-итератор представляет определённый адрес в контейнере.

В Lua итератор — это структура, поддерживающая типы указателей. Он может перебирать каждый элемент коллекции.


Итератор обобщённого for

Обобщённый for внутренне хранит функцию-итератор. Фактически он хранит три значения: функцию-итератор, константу состояния и управляющую переменную.

Итератор обобщённого for предоставляет пары ключ/значение коллекции. Синтаксис следующий:

for k, v in pairs(t) do
    print(k, v)
end

В приведённом выше коде k, v — это список переменных; pairs(t) — это список выражений.

Посмотрите на следующий пример:

Пример

array = {"Google", "Example"}

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:

Пример

function square(iteratorMaxCount,currentNumber)
   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 мы можем реализовать это так:

Пример

function iter (a, i)
    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


Многосостоятельные итераторы

Во многих случаях итераторам нужно хранить несколько фрагментов информации о состоянии, а не просто константу состояния и управляющую переменную. Простейший способ — использовать замыкания. Другой способ — инкапсулировать всю информацию о состоянии в таблицу и использовать эту таблицу как константу состояния итератора. В этом случае, поскольку всю информацию можно хранить в таблице, функция-итератор обычно не нуждается во втором параметре.

В следующем примере мы создали собственный итератор:

Пример

array = {"Google", "Example"}

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 использует функцию замыкания для вычисления размера коллекции и вывода каждого элемента.

Другие расширения