🎓 A1Learner
🔍
0 of 52 lessons completed

Lua Chaining Iterators

A chaining iterator is an iterator adapter that takes another iterator and returns a new iterator with changed values or fewer values. Adapters compose, so one adapter can feed the next.

A map adapter changes each value

The map adapter doubles each value from the source. The loop prints the changed values.

local function up_to(limit)
  local i = 0
  return function()
    i = i + 1
    if i <= limit then
      return i
    end
    return nil
  end
end
local function map_values(source, fn)
  return function()
    local v = source()
    if v == nil then
      return nil
    end
    return fn(v)
  end
end
local function double(x)
  return x * 2
end
for v in map_values(up_to(3), double) do
  print(v)
end

A filter adapter keeps only matching values

The filter adapter asks its test for each value. Only even values reach the loop.

local function up_to(limit)
  local i = 0
  return function()
    i = i + 1
    if i <= limit then
      return i
    end
    return nil
  end
end
local function filter_values(source, test)
  return function()
    while true do
      local v = source()
      if v == nil then
        return nil
      end
      if test(v) then
        return v
      end
    end
  end
end
local function is_even(x)
  return x % 2 == 0
end
for v in filter_values(up_to(5), is_even) do
  print(v)
end

Adapters compose into a chain

The filter output feeds the map adapter. Even numbers are doubled, and odd numbers never reach the map.

local function up_to(limit)
  local i = 0
  return function()
    i = i + 1
    if i <= limit then
      return i
    end
    return nil
  end
end
local function map_values(source, fn)
  return function()
    local v = source()
    if v == nil then
      return nil
    end
    return fn(v)
  end
end
local function filter_values(source, test)
  return function()
    while true do
      local v = source()
      if v == nil then
        return nil
      end
      if test(v) then
        return v
      end
    end
  end
end
local function is_even(x)
  return x % 2 == 0
end
local function double(x)
  return x * 2
end
local chained = map_values(filter_values(up_to(6), is_even), double)
for v in chained do
  print(v)
end

Example

local function up_to(limit)
  local i = 0
  return function()
    i = i + 1
    if i <= limit then
      return i
    end
    return nil
  end
end
local function map_values(source, fn)
  return function()
    local v = source()
    if v == nil then
      return nil
    end
    return fn(v)
  end
end
local function filter_values(source, test)
  return function()
    while true do
      local v = source()
      if v == nil then
        return nil
      end
      if test(v) then
        return v
      end
    end
  end
end
local function is_even(x)
  return x % 2 == 0
end
local function square(x)
  return x * x
end
local chained = map_values(filter_values(up_to(8), is_even), square)
for v in chained do
  print(v)
end

Key points

  • Adapter: a function that wraps an iterator and changes what it returns.
  • Map adapter: an adapter that changes each value before it is returned.
  • Filter adapter: an adapter that skips values that fail a test and returns only the rest.
  • Composition: feeding the output of one adapter into another adapter builds a longer chain.
  • One value at a time: each call to a chained iterator pulls only the next value through the chain.

In this example, the source counts from 1 to 8, the filter keeps only even values, and the map squares each kept value. The chain is built by feeding the filter output into the map adapter. The loop prints 4, 16, 36, and 64.

Note

a chain stays an iterator, so it works in a plain for loop with no extra step.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

You chain a filter for even numbers and then a map that doubles, over 1 to 5. What does the loop print?