Web Analytics

Iterators in Lua

Intermediate ~30 min read

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
Output
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
How it works:
  1. Factory returns: iterator function, invariant state, control variable
  2. For loop calls: iterator(state, control)
  3. Iterator returns new control variable and values
  4. 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
Output
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
Output
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
Output
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
Output
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
Output
Click Run to execute your code

Practice Exercise

Try these iterator challenges:

Output
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! πŸš€