This first edition was written for Lua 5.0. While still largely relevant for later versions, there are some differences.
The fourth edition targets Lua 5.3 and is available at Amazon and other bookstores.
By buying the book, you also help to support the Lua project.


7.5 – True Iterators

The name "iterator" is a little misleading, because our iterators do not iterate: What iterates is the for loop. Iterators only provide the successive values for the iteration. Maybe a better name would be "generator", but "iterator" is already well established in other languages, such as Java.

However, there is another way to build iterators wherein iterators actually do the iteration. When we use such iterators we do not write a loop; instead, we simply call the iterator with an argument that describes what the iterator must do at each iteration. More specifically, the iterator receives as argument a function that it calls inside its loop.

As a concrete example, let us rewrite once more the allwords iterator using this style:

    function allwords (f)
      -- repeat for each line in the file
      for l in io.lines() do
        -- repeat for each word in the line
        for w in string.gfind(l, "%w+") do
          -- call the function
          f(w)
        end
      end
    end
To use such iterator, we must supply the loop body as a function. If we only want to print each word, we simply use print:
    allwords(print)
More often, we use an anonymous function as the body. For instance, the next code fragment counts how many times the word "hello" appears in the input file:
    local count = 0
    allwords(function (w)
      if w == "hello" then count = count + 1 end
    end)
    print(count)
The same task, written with the previous iterator style, is not very different:
    local count = 0
    for w in allwords() do
      if w == "hello" then count = count + 1 end
    end
    print(count)

True iterators were popular in older versions of Lua, when the language did not have the for statement. How do they compare with generator-style iterators? Both styles have approximately the same overhead: one function call per iteration. On the one hand, it is easier to write the iterator with this second style (although we can recover this easiness with coroutines). On the other hand, the generator style is more flexible. First, it allows two or more parallel iterations. (For instance, consider the problem of iterating over two files comparing them word by word.) Second, it allows the use of break and return inside the iterator body. (With a true iterator, a return returns from the anonymous function, not from the function doing the iteration.)