New For |
|
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 block end
is equivalent to the following[1][2] (Lua 5.1)
do local _func, _state, var = <explist> while 1 do local var1, ..., varn = _func(_state, var1) var = var1 if var == nil then break end <block> end 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.
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(io.open(filename, "r")) return function () return f:read() or (assert(f:close()) and nil) end end for line in lines(file) do print(line) -- Or process line, etc. end
For those wondering about the anonymous function return value:
f:read()
reads a line, the part after the 'or' isn't evaluated.
f:read()
is nil
and we return the second part:
and nil
" forces this part to nil, so the loop ends.
I started to rewrite it like:
function lines(filename) local f = assert(io.open(filename, "r")) local i = 0 return function () i = i + 1 return f:read() or (assert(f:close()) and nil), i end end local line, number for line, number in lines(file) do print("(" .. number .. ") " .. line) end
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(io.open(filename, "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" end
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.