Before this lesson, complete lua-tables, lua-modules-errors.
This course runs standard Lua 5.5 with its standard library. Game engines, LuaJIT and embedded host APIs are outside this sequence.
A generic for loop repeatedly calls an iterator using iteration state and a control value. When the first returned result is nil, the loop ends. False is not the termination sentinel. Additional returned values become additional loop variables. The language also supports a closing value for resource-owning iteration, which is separate from this lesson's simple closure-based range.
Apply the contract
An iterator factory can hide its state in locals captured by a closure. For an inclusive range, initialize current to first minus step, advance once per call and return nil only after current exceeds last. Each factory call must create new locals, or two independent ranges will interfere. For this lab, endpoints and step are integers and step must be positive. An empty interval with first greater than last terminates without yielding a value.
local function counter(start)
local value=start
return function() value=value+1; return value end
end
local a,b=counter(0),counter(0)
print(a(),a(),b())Reason about the edge cases
Validate a zero or negative step before iteration begins; otherwise a loop can fail to make progress. Do not terminate merely because a valid returned value is false or zero. The build's output is numeric, but that general iterator rule becomes important in the next coroutine lesson. Test a one-element interval, a last value exactly on the step, a last bound between steps and two interleaved iterators. Inclusive boundaries and independent state are the core invariants. A closure is a useful small state machine whose transition should be clear from one call to the next.
Practice
Implement M.range(first,last,step) for integer bounds and a positive integer step. Return a closure suitable for generic for; it yields first, first+step, and so on through last inclusively, then nil. Empty ranges yield nothing. Each factory call has independent state. Reject a nonpositive step with an error.
Run sample starts again from the first public fixture and grants no mastery evidence. Submit checks all fixtures, including edge cases. Read the specification before editing; the bug and fill phases share the build’s contract. Flashcards reinforce recall on a separate schedule.