... until the first nil element.

An iterator is an object that can be used to traverse some or all elements in a standard template library container. Each iterator object represents a definite address in the container.

In Lua, an iterator is a structure that supports pointer types. It can traverse every element of a collection.


Generic for Iterator

Generic for stores the iterator function internally. In fact, it stores three values: the iterator function, the state constant, and the control variable.

The generic for iterator provides key/value pairs of the collection. The syntax is as follows:

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

In the above code, k, v are the variable list; pairs(t) is the expression list.

Look at the following example:

Example

array = {"Google", "Example"}

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

The output of the above code execution is:

1  Google
2  Example

In the above example, we used the iterator function ipairs provided by Lua by default.

Now let's look at the execution process of generic for:

  • First, initialize: evaluate the expression after `in`. The expression should return the three values needed by generic for: the iterator function, the state constant, and the control variable. As with multiple assignment, if the expression returns fewer than three values, the missing ones are automatically filled with nil, and extra values are ignored.
  • Second, call the iterator function with the state constant and control variable as arguments (note: for the for structure, the state constant is useless; it is only obtained during initialization and passed to the iterator function).
  • Third, assign the values returned by the iterator function to the variable list.
  • Fourth, if the first returned value is nil, the loop ends; otherwise, execute the loop body.
  • Fifth, return to the second step and call the iterator function again.

In Lua, we often use functions to describe iterators. Each time the function is called, it returns the next element of the collection. Lua iterators include the following two types:

  • Stateless iterators
  • Multi-state iterators

Stateless iterators

A stateless iterator is an iterator that does not retain any state. Therefore, in loops, we can use stateless iterators to avoid the extra cost of creating closures.

In each iteration, the iterator function is called with the values of two variables (the state constant and the control variable) as arguments. A stateless iterator can obtain the next element using only these two values.

A typical and simple example of such a stateless iterator is ipairs, which traverses each element of an array whose indices need to be numeric.

The following example uses a simple function to implement an iterator that computes the square of a number n:

Example

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

The output of the above example is:

1    1
2    4
3    9

The iteration state includes the table being traversed (the state constant, which does not change during the loop) and the current index (the control variable). ipairs and the iterator function are both very simple. In Lua, we can implement it like this:

Example

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

When Lua calls ipairs(a) to start the loop, it obtains three values: the iterator function iter, the state constant a, and the initial value of the control variable 0. Then Lua calls iter(a,0) and returns 1, a


Multi-state iterators

In many cases, iterators need to store multiple pieces of state information rather than simply a state constant and a control variable. The simplest method is to use closures. Another method is to encapsulate all state information into a table and use the table as the iterator's state constant. In this case, since all information can be stored in the table, the iterator function usually does not need a second parameter.

In the following example, we created our own iterator:

Example

array = {"Google", "Example"}

function elementIterator (collection)
   local index = 0
   local count = #collection
   -- closure function
   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

The output of the above example is:

Google
Example

In the above example, we can see that elementIterator uses a closure function to calculate the size of the collection and output each element.

Other extensions