Prerequisites:
A grasp on Lua along with programming fundamentals. While not necessary, reading Advanced Lua - Part I couldn't hurt.
Iterators:
An iterator is an object that allows a programmer to traverse through all the elements of a collection, regardless of its specific implementation. An example in C++:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> theVector; //data structure
vector<int>::iterator theIter; //iterator
//fill it
for(int i = 0; i < 5; i++){
theVector.push_back(i+2);
}
//access using the iterator
for(theIter = theVector.begin(); theIter != theVector.end(); theIter++) {
cout << *theIter << endl;
}
return 0;
}
In this case, the programmer uses the iterator as a pre-defined object. It is specific to the data structure to be traversed (which we specify ahead of time) and comes as part of the STL. "Pre-defined" is important to note because (in this case) the programmer does NOT modify the object in any way, nor defines its default behavior (the writer(s) of this data structure did). Without going too far down a rat-hole, also know that a for loop (when using the index variable to access elements) is iteration as well, just a different form (sans object):
for(int i = 0; i < 5; i++){
cout << someArray[i]; //iteration
}
In Lua, the generic for loops come with a built in iterator. The for loop's syntax is as follows:
for <var-list> in <exp-list> do
<body>
end
You've seen examples in previous tutorials, here are a few of the "built in" ones:
--[[
Returns three values: the next function, the table t, and nil,
so that the construction
for k,v in pairs(t) do body end
will iterate over all key–value pairs of table t.
]]--
t = {5, 10, 15}
for k, v in pairs(t) do
print (k, v)
end
--[[
Returns three values: an iterator function, the table t, and 0,
so that the construction:
for i,v in ipairs(t) do body end
will iterate over the pairs (1,t[1]), (2,t[2]), •••,
up to the first integer key absent from the table.
]]--
names = {"Knowles", "Sally", "Bill"}
for i, v in ipairs(names) do
print(i, v)
end
You might be asking yourself why is this advanced? Most intros to [insert language here] include for loops. Yes, that's true, but the for loop itself is not what we're looking at. The focus is on the actual iteration, the objects involved, and the manner of execution. Knowing this information will allow you to take full advantage of Lua. Let's write out what Lua is calling/doing in the above for loops in another way to see exactly how they work:
--[[
rewriting to show implementation
names = {"Knowles", "Sally", "Bill"}
for i, v in ipairs(names) do
print(i, v)
end
]]--
local 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
names = {"Knowles", "Sally", "Bill"}
for index, values in ipairs(names) do
print(values)
end
--[[
pairs() is essentially the same, but it calls the Lua function 'next'
rewriting to show implementation
t = {5, 10, 15}
for k, v in pairs(t) do
print (k, v)
end
]]--
function pairs(t)
return next, t, nil
end
names = {"Knowles", "Sally", "Bill"}
for index, values in pairs(names) do
print(values)
end
It is worth pointing out that these examples thus far are not true iterators. Meaning that the for loop does the actual iteration, the iterator only provides the successive values for the for loop. However, the concept is the same as in other programming languages (which call them iterators) so Lua kept the tradition. All of the above are "stateless" iterators. There are various ways to write complex state iterators (closures, tables, etc...), but they will not be covered in this tutorial.
The last two items cover how to write your own iterator. This example takes the concept of ipairs() and pairs() and modifies so it so that the return value is the value at the index rather then the index and value:
--creating your own iterator
function values(t)
local i = 0
return function () --anon function, creates a closure
i = i + 1
return t[i]
end
end
--example usage
myArray = {10, 20, 30}
for item in values(myArray) do --using our created iterator (for each)
print(item) --continues until nil is returned
end
Instead of two variables in the var list, we now have one, which is the actual value at each index in the table. This still requires a for loop to dictate its function, so let's rewrite it as a stand alone entity that takes a parameter of a function (in this case print()) and uses a for loop internally:
function values(t) local i = 0 return function () --anon function, creates a closure i = i + 1 return t[i] end end --rewritten to be its own entity function iter(func) for item in values(myArray) do func(item) end end --usage iter(print)
Nothing inherently changed in terms of functionality; we just moved it "inside" the iteration. Hopefully you found this both interesting and insightful. Happy coding!




MultiQuote


|