Generic for
Basic for loops have already been covered in loops. First, let's take a look at iterators. In layman's terms, an iterator is a function that returns the next set of values each time it is called. Here's a simple iterator:
-- returns an iterator that counts letters between first and last
function letterIterator(first, last)
-- store the position that the iterator is at
local index = first
-- return the iterator - a function!
return function()
if index <= last then
-- move to the next character
index = index + 1
-- return the ascii representation of the index plus 95 (a letter)
-- note that there's only one value being returned
return string.char(index + 95)
end
end
end
And here's how we can use it:
local iterator = letterIterator(1, 4)
local letter
letter = iterator()
print(letter) -- 'a'
letter = iterator()
print(letter) -- 'b'
letter = iterator()
print(letter) -- 'c'
letter = iterator()
print(letter) -- 'd'
letter = iterator()
print(letter) -- nil
letter = iterator()
print(letter) -- nil
As you can see, the iterator returned the first, second, third, and fourth letters, then stopped returning anything. At this point, there's nothing left to iterate, so you should stop calling the iterator. That's where the generic for loop comes in. We can write the previous code like this instead:
Discussion
The iterator is the number that keeps track of how many times a for loop is supposed to run. This is different from a the numeric for loop in Loops in that the numeric for loop there is simply an iterator:
Numeric for:
for i = 20, 1, -2 do
print(i)
end
However, in the generic for loop we get the values returned by the iterator function. In the iterator returned by letterIterator() above, we saw that string.char(index + 95), not index itself, was returned. Here's an example using multiple return values:
That pretty much covers generic for loops! Read on for some final examples!
Examples
ipairs
Here is an example using the standard library function ipairs:
Customized Iterators
You too can write your own iterators! Here is two useful examples:
string.gfind
This example returns multiple values:
descendants
This example iterates through all of the children of an instance:
function descendants(obj, depth)
assert(obj and obj.getChildren, "object parameter is missing or is not an instance")
local function yieldtree(obj, level)
if depth and level > depth then
return
end
for _, o in ipairs(obj:getChildren()) do
coroutine.yield(o, level)
yieldtree(o, level+1)
end
end
return coroutine.wrap(function() yieldtree(obj, 1) end)
end
pairs and ipairs
pairs and ipairs are iterator functions that return table indices and their corresponding values. pairs iterates through the entire table, even if the index is non-numerical (such as "Hi"). ipairs iterates through the table as if it were an array, starting at 1 and counting up consecutive indices. Also, please note that pairs does not iterate through the table in any particular order. Here's an example of the difference:
local sampleTable = {
[1] = "A",
[2] = 2,
[3] = "B",
[5] = 5,
Hi = "C"
}