Range Iterator |
|
for i=i,j,k do end
syntax. It can be used for example when the two or three parameters of the basic for loop are returned by another function. -- JeromeVuarand
function range(from, to, step) step = step or 1 return function(_, lastvalue) local nextvalue = lastvalue + step if step > 0 and nextvalue <= to or step < 0 and nextvalue >= to or step == 0 then return nextvalue end end, nil, from - step end
Example use:
function f() return 10, 0, -1 end for i in range(f()) do print(i) end
Update #1:
function range(from, to, step) step = step or 1 local f = step > 0 and function(_, lastvalue) local nextvalue = lastvalue + step if nextvalue <= to then return nextvalue end end or step < 0 and function(_, lastvalue) local nextvalue = lastvalue + step if nextvalue >= to then return nextvalue end end or function(_, lastvalue) return lastvalue end return f, nil, from - step end
Update #2:
-- range(a) returns an iterator from 1 to a (step = 1) -- range(a, b) returns an iterator from a to b (step = 1) -- range(a, b, step) returns an iterator from a to b, counting by step. function range(a, b, step) if not b then b = a a = 1 end step = step or 1 local f = step > 0 and function(_, lastvalue) local nextvalue = lastvalue + step if nextvalue <= b then return nextvalue end end or step < 0 and function(_, lastvalue) local nextvalue = lastvalue + step if nextvalue >= b then return nextvalue end end or function(_, lastvalue) return lastvalue end return f, nil, a - step end
-- Prints the range of numbers 1 to 10 inclusive. for i in range(10) do print(i) end
Update #3:
-- Some functions accept the returned values of generators (like ipairs()) -- and have a predefined for loop in their body like so: some_function = (...) local tmp = {} for k, v in ... do tmp[k] = v end return tmp end some_function(ipairs({ 1, 2, 3 }) -> { 1, 2, 3 } some_function(range(3)) -> { 1, 2, 3 }
The actual range() definition:
-- range(start) returns an iterator from 1 to a (step = 1) -- range(start, stop) returns an iterator from a to b (step = 1) -- range(start, stop, step) returns an iterator from a to b, counting by step. range = function (i, to, inc) if i == nil then return end -- range(--[[ no args ]]) -> return "nothing" to fail the loop in the caller if not to then to = i i = to == 0 and 0 or (to > 0 and 1 or -1) end -- we don't have to do the to == 0 check -- 0 -> 0 with any inc would never iterate inc = inc or (i < to and 1 or -1) -- step back (once) before we start i = i - inc return function () if i == to then return nil end i = i + inc return i, i end end
Why
to_table(range(10)) -- would create { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
-- print the sum of every cleanly-divisible-by-3 number from 1 to 100 print(setmetatable(to_table(range(100)), { __index == 'special_table' }):remove_if(function (x) return x % 3 ~= 0 end):reduce(function (x, y) return x + y --[[ sum ]] end, 0 --[[ initial y ]]))
Example calls:
for i in range( 10) print(i) end -- iterate 1 to 10, increment by 1 for i in range(-10) print(i) end -- iterate -1 to -10, decrement by 1 for i in range(7, -2) print(i) end -- iterate 7 to -2, decrement by 1 for i in range(3, 27, 3) print(i) end -- iterate 3 to 27, increment by 3 for i in range(0, 1, -1) print(i) end -- iterate 0 to 1, decrementing by 1 (loop forever downward) for i in range() print(i) end -- error() because the call to the "returned" iterator is a nil value
-- SleepyCoder