luaiterator

# How to pass an arbitrary iterator as a function argument?

##### Question

If I write a generic walk routine, why can I pass in my own iterators but not pairs?

##### Example

Here's a simple walk that does something for each item in src.

function walk(src)
for n,i in src do
print(n,  i[1]+i[#i]) end end

If I call this with walk(items(x)) then walk works fine.

function items(lst, n)
n=0
return function()
if lst and n < #lst then
n=n+1
return n,lst[n] end end end

x={
{1,2},
{3,4},
{5,6},
{5,6}}

walk(items(x)) --> prints out (1,3),(2,7),(3,11),etc

But I try the same with pairs(x), I get a crash.

walk(pairs(x))
lua: x.lua: bad argument #1 to 'for iterator' (table expected, got nil)
stack traceback:
[C]: in function 'next'
x.lua:7: in function 'walk'
x.lua:31: in main chunk
[C]: in ?

So what is different about pairs and my own iterator items?

Solution

• The pairs function returns 3 values:

1. The next function
2. The table
3. The initial key nil

The for-in statement uses the 3 values in an equivalent process as follows:

local f, t, k = pairs(x)
local v

repeat
k, v = f(t, k)
if k then
-- for body
end
until not k

Since the 3rd value is always nil, if you want the walk function to be effective on pairs, you need at least 2 arguments:

function walk(itor, t)
for n,i in itor, t do
print(n, i[1]+i[#i]) end end

You need 3 if you want it to be effective on any iterator functions.