New For

lua-users home

Abstract: This is a discussion of the extended iterator-style 'for' statement syntax that was introduced in Lua 5.0. This page is somewhat historical (and is no longer new). The reference manual[1] has a more complete description of this.


I (PhilippeLhoste) played a bit with the new 'for' syntax in Lua 5.0 (work0 in case the syntax/behavior change).

I started with the 2002/06/06 mail from Roberto, giving pseudo-code. Since I prefer long variable names, I rewrote it a bit for better (mine!) comprehension. Let me quote it here, as a reminder:

Pseudo code:

for var1, ..., varn in expList do

is equivalent to the following[1][2] (Lua 5.1)

  local _func, _state, var = <explist>
  while 1 do
    local var1, ..., varn = _func(_state, var1)
    var = var1
    if var == nil then break end

explist is two or three expressions: a function (_func), a state (for persistence of data) and an initial value. It can be a function returning these values.

_func, returned by explist, must return n values, the first one being nil when process is exhausted.

I tried with the following code (also provided by Roberto):

t = { "a", "b", "c" }
for i, k in nexti(t) do print(i, k) end
for k, v in next, t do print(k, v) end
for k, v in t do print(k, v) end

The results are identical.

In the first line, 'nexti' returns the function iterator and the table given as parameter.

In the second line, 'next' is the function (_func) and 't' is the state (_state).

The third line is syntactic sugar for the above lines, to ensure backward compatibility.

Application: reading the lines of a file

The code snippet given by Roberto to read the lines of a file helped me understanding the pseudo-code above (I am a bit slow...).

It was:

function lines(filename)
  local f = assert(, "r"))
  return function ()
    return f:read() or (assert(f:close()) and nil)

for line in lines(file) do
  print(line) -- Or process line, etc.

For those wondering about the anonymous function return value:

- If f:read() reads a line, the part after the 'or' isn't evaluated.

- Else (end of line) f:read() is nil and we return the second part:

If assert is OK, it returns its expression.

In any case, the "and nil" forces this part to nil, so the loop ends.

Playing with the 'for' syntax

I started to rewrite it like:

function lines(filename)
  local f = assert(, "r"))
  local i = 0
  return function ()
    i = i + 1
    return f:read() or (assert(f:close()) and nil), i

local line, number
for line, number in lines(file) do
  print("(" .. number .. ") " .. line)

We already have a state persistence... I suppose this is the magic of 5.0's closures, the 'i' in the anonymous function pointing to the local variable in lines().

I made a final version, using state and initial value as per the pseudo-code:

function lines(filename)
  local f = assert(, "r"))
  local state = {}
  state.persistentValue = " "
  state.counter = 0
  return function (_state, _previousValue)
    _state.persistentValue = "." .. _state.persistentValue
    _state.counter = _state.counter + 1
    print(_state.persistentValue .. _previousValue)
    return f:read() or (assert(f:close()) and nil), _state.counter
  end, state, "First value"

But I am no longer sure of the advantage of this state over the previous version... Well, it looks more like OO, and I am probably missing some side effect.

RobertoIerusalimschy answered: This is a matter of taste. The big advantage of using the state is when you don't need to create any new "object" (table, closure, etc.) to run the for. The `nexti' is an example. In "heavier" loops, the cost of an extra object is negligible. E.g., in the file example, you already have to open the file, create a file handler, create several strings (the lines), etc. etc. An extra closure (or table) makes little difference to the total bill.

See Also

RecentChanges · preferences
edit · history
Last edited March 29, 2008 8:58 pm GMT (diff)