Lua Iterators
An iterator is an object that can be used to traverse parts or all of the elements in a standard template library container, with each iterator object representing a specific address within the container.
In Lua, an iterator is a structure that supports pointer types and can traverse each element of a collection.
Generic For Iterator
The generic for internally saves the iteration function, actually storing three values: the iteration function, a state constant, and a control variable.
The generic for iterator provides key/value pairs of the collection, with the syntax as follows:
for k, v in pairs(t) do
print(k, v)
end
In the above code, k, v
is the variable list; pairs(t)
is the expression list.
Consider the following example:
Example
array = {"Google", "tutorialpro"}
for key,value in ipairs(array)
do
print(key, value)
end
The output of the above code is:
1 Google
2 tutorialpro
In the above example, we used the default iterator function ipairs
provided by Lua.
Let's look at the execution process of the generic for:
First, initialization, compute the value of the expression after
in
, which should return the three values needed by the generic for: iteration function, state constant, and control variable; similar to multi-value assignment, if the result returns fewer than three values, they will be automatically padded withnil
, and extra values will be ignored.Second, call the iteration function with the state constant and control variable as parameters (note: for the for structure, the state constant is not useful, it is just obtained and passed to the iteration function during initialization).
Third, assign the values returned by the iteration 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 iteration function again.
In Lua, we often use functions to describe iterators, with each call to the function returning the next element of the collection. Lua iterators include the following types:
- Stateless iterators
- Stateful iterators
Stateless Iterators
Stateless iterators are those that do not retain any state, thus allowing us to avoid the extra cost of creating closures in loops by using stateless iterators.
Each iteration, the iteration function is called with two variables (state constant and control variable) as parameters, and a stateless iterator can obtain the next element using only these two values.
A typical simple example of such a stateless iterator is ipairs
, which traverses each element of the array, with the element index needing to be numeric.
The following example uses a simple function to implement an iterator that calculates 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 state of the iteration includes the table being traversed (a state constant that does not change during the loop) and the current index (control variable). Both ipairs
and the iteration function are simple, and in Lua, we can implement them 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 iteration function iter
, the state constant a
, and the initial value of the control variable 0
; then Lua calls iter(a,0)
returning 1, a[1]
(unless a[1]=nil
); the second iteration calls iter(a,1)
returning 2, a[2]
... until the first nil
element.
Stateful Iterators
In many cases, an iterator needs to save multiple state information rather than simple state constants and control variables. The simplest method is to use closures, and another method is to encapsulate all state information into a table, making the table the state constant of the iterator, as this allows all information to be stored in the table, the iteration function usually does not need a second parameter.
The following example creates our own iterator:
Example
array = {"Google", "tutorialpro"}
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
tutorialpro
In the above example, we can see that elementIterator
uses a closure function to calculate the size of the collection and output each element.