Page 1 of 1

Advanced Lua - Part II Iterations, Iterations, Iterations...

#1 KYA  Icon User is offline

  • g++ jameson.cpp -o beverage
  • member icon

Reputation: 3089
  • View blog
  • Posts: 19,137
  • Joined: 14-September 07

Posted 25 August 2009 - 06:13 PM

Advanced Lua - Part II

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!

Is This A Good Question/Topic? 0
  • +

Page 1 of 1