Iterators in Lua
Iterators are functions that allow you to traverse collections in a controlled way. Lua's generic for loop works with iterators to provide elegant iteration patterns. Understanding how iterators work enables you to create custom iteration logic for any data structure. Let's explore how to create and use iterators effectively!
The Iterator Protocol
The generic for loop expects an iterator function:
for var1, var2, ... in iterator do
-- body
end
The iterator function is called repeatedly, returning values until it returns
nil:
-- Simple iterator
local function simpleIterator()
local i = 0
return function()
i = i + 1
if i <= 3 then
return i
end
end
end
for value in simpleIterator() do
print(value) -- 1, 2, 3
end
Click Run to execute your code
Stateless Iterators
Stateless iterators don't maintain state between calls. They receive the state from the for loop:
-- Iterator function
local function iter(t, i)
i = i + 1
local v = t[i]
if v then
return i, v
end
end
-- Factory function
local function values(t)
return iter, t, 0
end
-- Usage
local fruits = {"apple", "banana", "cherry"}
for i, v in values(fruits) do
print(i, v)
end
- Factory returns: iterator function, invariant state, control variable
- For loop calls:
iterator(state, control) - Iterator returns new control variable and values
- Loop continues until iterator returns
nil
ipairs Implementation
Here's how ipairs works internally:
local function myIpairs(t)
local function iter(t, i)
i = i + 1
local v = t[i]
if v ~= nil then
return i, v
end
end
return iter, t, 0
end
local arr = {10, 20, 30}
for i, v in myIpairs(arr) do
print(i, v)
end
Click Run to execute your code
Stateful Iterators
Stateful iterators use closures to maintain state:
local function range(from, to, step)
step = step or 1
local current = from - step
return function()
current = current + step
if current <= to then
return current
end
end
end
-- Usage
for i in range(1, 10, 2) do
print(i) -- 1, 3, 5, 7, 9
end
Fibonacci Iterator
local function fibonacci(n)
local a, b = 0, 1
local count = 0
return function()
if count < n then
count = count + 1
a, b = b, a + b
return a
end
end
end
for num in fibonacci(10) do
print(num) -- 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
end
Click Run to execute your code
Custom Iterators
Words Iterator
local function words(str)
local pos = 1
return function()
local start, finish = str:find("%w+", pos)
if start then
pos = finish + 1
return str:sub(start, finish)
end
end
end
local text = "Hello World from Lua"
for word in words(text) do
print(word)
end
Lines Iterator
local function lines(str)
local pos = 1
return function()
if pos > #str then return nil end
local start = pos
local finish = str:find("\n", pos) or #str + 1
pos = finish + 1
return str:sub(start, finish - 1)
end
end
local text = "Line 1\nLine 2\nLine 3"
for line in lines(text) do
print(line)
end
Reverse Iterator
local function reverse(t)
local i = #t + 1
return function()
i = i - 1
if i > 0 then
return i, t[i]
end
end
end
local arr = {10, 20, 30, 40}
for i, v in reverse(arr) do
print(i, v) -- 4,40 3,30 2,20 1,10
end
Click Run to execute your code
Advanced Iterator Patterns
Filter Iterator
local function filter(predicate, iterator, state, var)
return function()
while true do
local value
var, value = iterator(state, var)
if var == nil then return nil end
if predicate(value) then
return var, value
end
end
end, state, var
end
-- Usage
local numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for i, v in filter(function(x) return x % 2 == 0 end, ipairs(numbers)) do
print(i, v) -- Even numbers only
end
Map Iterator
local function map(func, iterator, state, var)
return function()
local value
var, value = iterator(state, var)
if var == nil then return nil end
return var, func(value)
end, state, var
end
-- Usage
local numbers = {1, 2, 3, 4, 5}
for i, v in map(function(x) return x * 2 end, ipairs(numbers)) do
print(i, v) -- Doubled values
end
Take Iterator
local function take(n, iterator, state, var)
local count = 0
return function()
if count >= n then return nil end
count = count + 1
return iterator(state, var)
end, state, var
end
-- Usage
local function infiniteOnes()
return function() return 1 end
end
for value in take(5, infiniteOnes()) do
print(value) -- 1, 1, 1, 1, 1
end
Click Run to execute your code
Practical Examples
Tree Traversal
local function traverse(node)
local stack = {node}
return function()
if #stack == 0 then return nil end
local current = table.remove(stack)
-- Add children to stack
if current.children then
for i = #current.children, 1, -1 do
table.insert(stack, current.children[i])
end
end
return current
end
end
local tree = {
value = 1,
children = {
{value = 2},
{value = 3, children = {{value = 4}, {value = 5}}}
}
}
for node in traverse(tree) do
print(node.value)
end
Pagination Iterator
local function paginate(items, pageSize)
local page = 0
local totalPages = math.ceil(#items / pageSize)
return function()
if page >= totalPages then return nil end
page = page + 1
local start = (page - 1) * pageSize + 1
local finish = math.min(page * pageSize, #items)
local pageItems = {}
for i = start, finish do
table.insert(pageItems, items[i])
end
return page, pageItems
end
end
local items = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
for page, pageItems in paginate(items, 3) do
print("Page " .. page .. ":", table.concat(pageItems, ", "))
end
Click Run to execute your code
Practice Exercise
Try these iterator challenges:
Click Run to execute your code
Summary
In this lesson, you learned:
- The iterator protocol and how generic for loops work
- Stateless iterators that receive state from the loop
- Stateful iterators using closures
- Custom iterators for specific use cases
- Advanced patterns: filter, map, take
- Practical examples: tree traversal, pagination
What's Next?
Congratulations on completing Module 4! You've mastered tables, the table library, metatables, and iteratorsβthe core of Lua's data manipulation capabilities. Next, we'll explore Object-Oriented Programming in Lua. You'll learn how to create classes, implement inheritance, and build robust OOP systems using tables and metatables. Let's continue! π
Enjoying these tutorials?