Lorinc Nyitrai
Lorinc Nyitrai

Reputation: 960

Why this works in Lua? - for k, v in next, t, nil do print(k, v) end

I just can not wrap my head around this working Lua snipplet.

This:

t = {'a', 'b', 'c'}
for k, v in next, t, nil do
  print(k, v)
end

Returns this:

1   a
2   b
3   c

Can someone explain,

Upvotes: 4

Views: 2915

Answers (2)

nobody
nobody

Reputation: 4264

To expand a bit on the other answers:

A more descriptive naming of the values involved in a 'generic for loop' is the following:

for k, v1, v2, … in f_step, state, k0 do  …  end

and it goes in a loop like

k,v1,v2,…=f_step(state,k0) ; if k==nil then break end ; k0=k ; --(…for body here…)
k,v1,v2,…=f_step(state,k0) ; if k==nil then break end ; k0=k ; --(…for body here…)
k,v1,v2,…=f_step(state,k0) ; if k==nil then break end ; k0=k ; --(…for body here…)
…

and f_step is free to modify state in any way it likes (though pairs/next don't do that, and as yet another example, string.gmatch even ignores both state and k entirely and keeps all varying state in a closure (think 'function' if you don't know that term yet) instead.)


Now, what pairs does is essentially just

function pairs( t )
    -- (__pairs logic goes here, omitted for brevity)
    return next, t, nil
end

and the common

for k, v in pairs( t ) do  …  end

essentially expands to

for k, v in next, t, nil do  …  end

(unless t has a metatable with __pairs.)

Now there's two reasons why you might explicitly write next, t, nil instead of pairs – to confuse people who don't know about this yet, or to avoid triggering __pairs. (To avoid triggering __index/__newindex, you have rawget/rawset, to avoid __pairs, you explicitly write next, t, nil. Or maybe you define function rawpairs( t ) return next, t, nil end and use that…)

Upvotes: 7

Lorinc Nyitrai
Lorinc Nyitrai

Reputation: 960

All credits go to Egor for his answer among the comments.

From the Lua 5.3 manual:

A for statement like

 for var_1, ···, var_n in explist do block end 

is equivalent to the code:

 do
   local f, s, var = explist
   while true do
     local var_1, ···, var_n = f(s, var)
     if var_1 == nil then break end
     var = var_1
     block
   end
 end

So the original statement translates into an infinite while loop, that keeps calling next() with initial parameters next(t, nil), and in each iteration the second parameter is replaced by the next index in the t table. When finally next(t, index_n) returns nil, the loop breaks.

This seems to me an extremely powerful way to traverse a table, as next() can be replaced by pretty much any function giving full control over the iteration. Wow.

Upvotes: 3

Related Questions