Menu
CoddyTech

Iterator Pattern

Part of the Object Oriented Programming section of Coddy's Lua journey. Lesson 64 of 70.

When you have a collection of items stored inside an object, you often want to loop through them. Lua's generic for loop works with any iterator function: it calls the function before every pass and stops when the function returns nil.

The Iterator pattern gives the object a method that returns such a function. The function keeps its position in a local variable, so the position belongs to one loop, not to the collection:

local Collection = {}
Collection.__index = Collection

function Collection:new()
    local obj = { items = {} }
    setmetatable(obj, Collection)
    return obj
end

function Collection:add(item)
    table.insert(self.items, item)
end

function Collection:iter()
    local index = 0
    return function()
        index = index + 1
        return self.items[index]
    end
end

Each call to bag:iter() creates a new function with its own index, starting at 0. The function is a closure: it can still use index and self after iter has returned. Past the last item, self.items[index] is nil, and the loop stops:

local bag = Collection:new()
bag:add("apple")
bag:add("banana")

for item in bag:iter() do
    print(item)  -- apple, then banana
end

for item in bag:iter() do
    print(item)  -- apple, then banana again
end

Keep the position out of the collection itself. If the object stored self.index and acted as its own iterator, the first loop would leave the index at the end, so a second loop would print nothing, and a loop that stopped early with break would make the next loop start in the middle.

challenge icon

Challenge

Easy

Let's build a Playlist class that you can loop through with Lua's generic for loop! You'll implement the Iterator pattern with a :iter() method that returns an iterator function.

You'll organize your code across two files:

  • Playlist.lua: Create a Playlist class that stores songs internally. Your class needs a :new() constructor that initializes an empty songs list and an :addSong(title) method that adds a song to it. Most importantly, add an :iter() method that returns a new function each time it is called. That function keeps its position in a local variable and returns the next song on each call, or nil when there are no more songs.
  • main.lua: Require your Playlist module and create a new playlist. Add songs to it based on the inputs you receive, then use a generic for loop over playlist:iter() to print each song title on its own line.

Because every call to :iter() starts at the first song, the same playlist can be looped over any number of times.

You will receive three inputs, each representing a song title to add to your playlist (in order).

For example, if the inputs are Bohemian Rhapsody, Stairway to Heaven, and Hotel California, the output should be:

Bohemian Rhapsody
Stairway to Heaven
Hotel California

If the inputs are Yesterday, Imagine, and Let It Be, the output should be:

Yesterday
Imagine
Let It Be

Try it yourself

-- Require the Playlist module
local Playlist = require('Playlist')

-- Read three song titles from input
local song1 = io.read()
local song2 = io.read()
local song3 = io.read()

-- TODO: Create a new playlist using Playlist:new()

-- TODO: Add the three songs to the playlist using :addSong()

-- TODO: Use a generic for loop over playlist:iter()
-- and print each song title on its own line
-- Hint: for song in playlist:iter() do ... end
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online Lua compiler