Generic for

A Generic For loop is a type of for loop that takes advantage of iterator functions. You should understand basic for loops before learning this.

Stateful iterators[edit]

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 function that generates a simple iterator:

Select
-- 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:

for letter in letterIterator(1, 4) do
	print(letter)
end
a
b
c
d

Explanation[edit]

The iterator is the object 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 index:

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:

1 6
2 8
3 10
function doublingIterator(array)
	local i = 1 -- The index in the array we're currently at
	return function()
		-- Make sure we haven't hit the end of the array
		if array[i] ~= nil then
			-- prepare return values
			local index, doubledValue = i, array[i]*2
			-- Add one to i, so that we use the next element next time
			i = i + 1
			return index, doubledValue
		end
	end
end
 
local numbers = {3, 4, 5}
 
-- There are two variables, because two values are returned!
for index, doubledValue in doublingIterator(numbers) do
	print(index, doubledValue)
end

Standard Library Iterators[edit]

Two commonly-used standard library iterator functions are pairs and ipairs. These functions return table keys and their corresponding values. pairs iterates through the entire table, even if the key is non-numerical (such as "Hi"), treating it as a dictionary. ipairs iterates through the table as if it were an array, starting at 1 and counting up consecutive integer indices. Once it hits a nil value, it will stop looping through the table. 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"
}


for i, v in ipairs(sampleTable) do
	print(i, v)
end
1	A
2	2
3	B
for k, v in pairs(sampleTable) do
	print(k, v)
end
2	2
3	B
1	A
5	5
Hi	C

Since pairs uses next as its iterator function, some people prefer to call it directly:

for k, v in next, sampleTable do
	print(k, v)
end
2	2
3	B
1	A
5	5
Hi	C


Customized Iterators[edit]

You too can write your own iterators! Here are two useful examples:

string.gfind[edit]

This example returns multiple values:

string = {}
string.gfind = function(stringToSearch, pattern, start)
	local start = start or 1 -- Default value is 1
	return function()
		local beginning, ending = stringToSearch:find(pattern, start) -- Start searching at the specified location
		if beginning and ending then -- Check to make sure that the match is there
			start = ending + 1 -- Add one to the ending so the pattern will start to look after the last match
			return beginning, ending, stringToSearch:sub(beginning, ending) -- return the 3 values
		end
	end
end
 
local stringToSearch = "Hello! My name is merlin1188!"
 
for start, finish, value in string.gfind(stringToSearch, "%a+") do
	print("The match starts at " .. start ..", finishes at " .. finish .. ", and is " .. value)
end
The match starts at 1, finishes at 5, and is Hello
The match starts at 8, finishes at 9, and is My
The match starts at 11, finishes at 14, and is name
The match starts at 16, finishes at 17, and is is
The match starts at 19, finishes at 24, and is merlin





descendants[edit]

This example iterates through all of the children of an instance:

Select
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)

External links[edit]