Reading — step 1 of 5
Learn
Iterators and the Generic for
You have used the counting for already:
for i = 1, 3 do io.write(i, " ") end
--> 1 2 3
That form needs to know the bounds up front. The other form — the generic
for — asks something else for the next value until it runs out, and it is how
you walk a table whose size and keys you do not control.
ipairs: the array part, in order
local t = {"a", "b", "c"}
for i, v in ipairs(t) do io.write(i, "=", v, " ") end
--> 1=a 2=b 3=c
ipairs starts at 1 and stops the moment it finds a missing index. That word
stops is load-bearing:
local holes = {"a", nil, "c"}
for i, v in ipairs(holes) do io.write(i, "=", v, " ") end
print("| # is " .. #holes)
--> 1=a | # is 3
ipairs gave up after the first element, while #holes claims 3. A table with a
hole in its array part has no well-defined length in Lua, and these two operators
disagree about it in exactly the way that ruins an afternoon. Keep array parts
contiguous.
pairs: every key, in no particular order
local d = {x = 1, y = 2}
local keys = {}
for k, v in pairs(d) do keys[#keys + 1] = k .. "=" .. v end
table.sort(keys)
print(table.concat(keys, " "))
--> x=1 y=2
Note the table.sort. pairs order is unspecified — the manual promises
nothing, and it can differ between runs. Any output that must be stable has to
sort the keys itself. That is not a wart to work around; it follows from a hash
table having no natural order.
pairs also sees the array part, so a table with both gives you everything:
local mixed = {"one", name = "two"}
local n = 0
for _ in pairs(mixed) do n = n + 1 end
print(n)
--> 2
Writing your own
The generic for has no idea what ipairs is. It just calls a function
repeatedly until that function returns nil. A function that remembers a
variable from around it — a closure — therefore already is an iterator, and
the Closures lesson in the next chapter takes the idea further:
local function upto(limit)
local i = 0
return function()
i = i + 1
if i <= limit then return i, i * i end
end
end
for i, sq in upto(4) do io.write(i, ":", sq, " ") end
--> 1:1 2:4 3:9 4:16
The closure keeps i between calls; returning nothing ends the loop. That is the
entire contract.
Common mistakes
- Using
ipairson a table with holes — it stops at the first gap, silently returning a prefix of your data. - Trusting
pairsorder — sort the keys when the output must be stable. - Using
#on a non-contiguous table — it is only meaningful when indices run 1..n with no gaps. - Forgetting the loop ends on
nil— an iterator that returns a falsy-but-present value likefalsestill continues; one that returns nothing stops. pairswhen you meantipairs—pairson a list also yields the string keys somebody added later.
Your exercise
Every Nth asks you to write the iterator yourself, not to call one.
The first line holds two numbers, n and k. The next n lines are values.
Write stepper(t, k), returning a closure that yields k, t[k], then
2k, t[2k], and so on while the index is within the table — and returns nothing
once it is past the end. The driver walks it with a generic for and prints
index:value for each.
With 5 values and k of 2 you print positions 2 and 4 only. The closure has to
remember where it was between calls; that is the whole job.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…