Lua - Iteradores

Iterator é uma construção que permite percorrer os elementos da chamada coleção ou contêiner. Em Lua, essas coleções geralmente se referem a tabelas, que são usadas para criar várias estruturas de dados, como array.

Genérico para Iterator

Um genérico para iterador fornece os pares de valores-chave de cada elemento na coleção. Um exemplo simples é fornecido abaixo.

array = {"Lua", "Tutorial"}

for key,value in ipairs(array) 
do
   print(key, value)
end

Quando executarmos o código acima, obteremos a seguinte saída -

1  Lua
2  Tutorial

O exemplo acima usa a função iteradora ipairs padrão fornecida por Lua.

Em Lua, usamos funções para representar iteradores. Com base na manutenção de estado nessas funções iterativas, temos dois tipos principais -

  • Iteradores sem estado
  • Iteradores com estado

Iteradores sem estado

Pelo próprio nome podemos entender que este tipo de função iteradora não retém nenhum estado.

Vejamos agora um exemplo de criação de nosso próprio iterador usando uma função simples que imprime os quadrados de n números.

function square(iteratorMaxCount,currentNumber)

   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
      return currentNumber, currentNumber*currentNumber
   end
	
end

for i,n in square,3,0
do
   print(i,n)
end

Quando executarmos o programa acima, obteremos a seguinte saída.

1	1
2	4
3	9

O código acima pode ser ligeiramente modificado para imitar a maneira como a função ipairs dos iteradores funciona. É mostrado abaixo.

function square(iteratorMaxCount,currentNumber)

   if currentNumber<iteratorMaxCount
   then
      currentNumber = currentNumber+1
      return currentNumber, currentNumber*currentNumber
   end
	
end

function squares(iteratorMaxCount)
   return square,iteratorMaxCount,0
end  

for i,n in squares(3)
do 
   print(i,n)
end

Quando executarmos o programa acima, obteremos a seguinte saída.

1	1
2	4
3	9

Iteradores com estado

O exemplo anterior de iteração usando função não retém o estado. Cada vez que a função é chamada, ela retorna o próximo elemento da coleção com base em uma segunda variável enviada para a função. Para manter o estado do elemento atual, fechamentos são usados. O fechamento retém os valores das variáveis ​​nas chamadas de funções. Para criar um novo fecho, criamos duas funções, incluindo o próprio fecho e uma fábrica, a função que cria o fecho.

Vamos agora ver um exemplo de criação de nosso próprio iterador no qual usaremos fechamentos.

array = {"Lua", "Tutorial"}

function elementIterator (collection)

   local index = 0
   local count = #collection
	
   -- The closure function is returned
	
   return function ()
      index = index + 1
		
      if index <= count
      then
         -- return the current element of the iterator
         return collection[index]
      end
		
   end
	
end

for element in elementIterator(array)
do
   print(element)
end

Quando executarmos o programa acima, obteremos a seguinte saída.

Lua
Tutorial

No exemplo acima, podemos ver que elementIterator tem outro método interno que usa o índice de variáveis ​​externas locais e contagem para retornar cada um dos elementos na coleção, incrementando o índice cada vez que a função é chamada.

Podemos criar nossos próprios iteradores de função usando o fechamento conforme mostrado acima e ele pode retornar vários elementos para cada vez que iteramos na coleção.