New For

lua-users home
wiki

Difference (from prior major revision) (no other diffs)

Changed: 1c1
I (PhilippeLhoste) played a bit with the new 'for' syntax in Lua 5.0 (work0 in case the syntax/behavior change).
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.

Added: 4a5,6
I (PhilippeLhoste) played a bit with the new 'for' syntax in Lua 5.0 (work0 in case the syntax/behavior change).


Changed: 10c12
{{{
{{{!Lua

Changed: 16c18
is equivalent to
is equivalent to the following[1][2] (Lua 5.1)

Changed: 18c20
{{{
{{{!Lua

Changed: 20,21c22
local _func, _state, var1 = expList
local var2, ..., varn
local _func, _state, var = <explist>

Changed: 23,27c24,27
var1, ..., varn = _func(_state, var1)
if var1 == nil then
break
end
block
local var1, ..., varn = _func(_state, var1)
var = var1
if var == nil then break end
<block>

Changed: 32c32
expList is two or three expressions: a function (_func), a state (for persistance of data) and an initial value. It can be a function returning these values.
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.

Changed: 34c34
_func, returned by expList, must return n values, the first one being nil when process is exhausted.
_func, returned by explist, must return n values, the first one being nil when process is exhausted.

Changed: 37c37,38
{{{

{{{!Lua

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

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

Changed: 58c59
{{{
{{{!Lua

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

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

Changed: 77c78
If assert is OK, it returns its expression.
If assert is OK, it returns its expression.

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

Changed: 85c86
{{{
{{{!Lua

Changed: 101c102
We already have a state persistance... I suppose this is the magic of 5.0's closures, the 'i' in the anonymous function pointing to the local variable in lines().
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().

Changed: 105c106
{{{
{{{!Lua

Changed: 123c124
RobertoIerusalimschy answered:
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.

Changed: 125,132c126
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 ===

Changed: 134,135c128,129
----
NOTES
* LuaList:2002-06/msg00326.html
* LuaList:2002-06/msg00343.html - nexti

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.

Introduction

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.

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(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:

- 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(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.

See Also


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