Lua - Iteratori

Iterator è un costrutto che consente di attraversare gli elementi della cosiddetta raccolta o contenitore. In Lua, queste raccolte fanno spesso riferimento a tabelle, che vengono utilizzate per creare varie strutture di dati come array.

Generico per iteratore

Un generico per iteratore fornisce le coppie chiave-valore di ogni elemento nella raccolta. Di seguito viene fornito un semplice esempio.

array = {"Lua", "Tutorial"}

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

Quando eseguiamo il codice sopra, otterremo il seguente output:

1  Lua
2  Tutorial

L'esempio precedente utilizza la funzione iteratore ipairs predefinita fornita da Lua.

In Lua usiamo le funzioni per rappresentare gli iteratori. Sulla base del mantenimento dello stato in queste funzioni iteratore, abbiamo due tipi principali:

  • Iteratori apolidi
  • Iteratori con stato

Iteratori apolidi

Dal nome stesso possiamo capire che questo tipo di funzione iteratore non mantiene alcuno stato.

Vediamo ora un esempio di creazione del nostro iteratore utilizzando una semplice funzione che stampa i quadrati di n numeri.

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 eseguiamo il programma precedente, otterremo il seguente output.

1	1
2	4
3	9

Il codice sopra può essere leggermente modificato per imitare il modo in cui funzionano le funzioni ipairs degli iteratori. È mostrato di seguito.

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 eseguiamo il programma precedente, otterremo il seguente output.

1	1
2	4
3	9

Iteratori con stato

L'esempio precedente di iterazione utilizzando la funzione non mantiene lo stato. Ogni volta che la funzione viene chiamata, restituisce l'elemento successivo della raccolta in base a una seconda variabile inviata alla funzione. Per mantenere lo stato dell'elemento corrente, vengono utilizzate le chiusure. La chiusura conserva i valori delle variabili tra le chiamate di funzioni. Per creare una nuova chiusura, creiamo due funzioni tra cui la chiusura stessa e una fabbrica, la funzione che crea la chiusura.

Vediamo ora un esempio di creazione del nostro iteratore in cui useremo le chiusure.

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 eseguiamo il programma precedente, otterremo il seguente output.

Lua
Tutorial

Nell'esempio sopra, possiamo vedere che elementIterator ha un altro metodo all'interno che utilizza l'indice e il conteggio delle variabili esterne locali per restituire ciascuno degli elementi nella raccolta incrementando l'indice ogni volta che viene chiamata la funzione.

Possiamo creare i nostri iteratori di funzioni usando la chiusura come mostrato sopra e può restituire più elementi per ciascuna delle volte in cui iteriamo nella raccolta.