Lua - Iteradores

Iterator es una construcción que le permite atravesar los elementos de la denominada colección o contenedor. En Lua, estas colecciones a menudo se refieren a tablas, que se utilizan para crear varias estructuras de datos como matrices.

Genérico para iterador

Un iterador genérico para proporciona los pares clave-valor de cada elemento de la colección. A continuación se ofrece un ejemplo sencillo.

array = {"Lua", "Tutorial"}

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

Cuando ejecutamos el código anterior, obtendremos el siguiente resultado:

1  Lua
2  Tutorial

El ejemplo anterior usa la función de iterador ipairs predeterminada proporcionada por Lua.

En Lua usamos funciones para representar iteradores. Según el mantenimiento del estado en estas funciones de iterador, tenemos dos tipos principales:

  • Iteradores sin estado
  • Iteradores con estado

Iteradores sin estado

Por el nombre mismo podemos entender que este tipo de función iteradora no retiene ningún estado.

Veamos ahora un ejemplo de cómo crear nuestro propio iterador usando una función simple que imprime los cuadrados 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

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado.

1	1
2	4
3	9

El código anterior se puede modificar ligeramente para imitar la forma en que funciona la función ipairs de los iteradores. Se muestra a continuación.

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

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado.

1	1
2	4
3	9

Iteradores con estado

El ejemplo anterior de iteración que usa la función no retiene el estado. Cada vez que se llama a la función, devuelve el siguiente elemento de la colección basándose en una segunda variable enviada a la función. Para mantener el estado del elemento actual, se utilizan cierres. El cierre retiene los valores de las variables en las llamadas a funciones. Para crear un nuevo cierre, creamos dos funciones, incluido el cierre en sí y una fábrica, la función que crea el cierre.

Veamos ahora un ejemplo de cómo crear nuestro propio iterador en el que usaremos cierres.

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

Cuando ejecutamos el programa anterior, obtendremos el siguiente resultado.

Lua
Tutorial

En el ejemplo anterior, podemos ver que elementIterator tiene otro método dentro que usa el índice y el recuento de variables externas locales para devolver cada uno de los elementos de la colección aumentando el índice cada vez que se llama a la función.

Podemos crear nuestros propios iteradores de función usando el cierre como se muestra arriba y puede devolver múltiples elementos para cada una de las veces que iteramos a través de la colección.