local exports = {}Map, filter and transform lazy iterators.
iter offers the familiar map(), filter(), etc but with a twist: rather than transforming tables, iter transforms the iterator. Transformations are lazy and no work is done until iterator is consumed (usually with a for loop). This is faster and more memory efficient, since items are transformed one-by-one as iterator is consumed and no interim tables are created.
Transform iterator functions using familiar map, filter, reduce, etc.
Transformations are lazy and are only performed item-by-item when the final
iterator is consumed.
local exports = {}Create the metatable we use for iter_values below.
local _iter_values_of = {}
function _iter_values_of.__call(self)
if not self.is_exhausted then
local i, v = self.next(self.state, self.i)
if not i then
self.is_exhausted = true
else
self.i = i
return v
end
end
endCapture the state of a stateless iterator and return a stateful iterator
which will only return values. This is a lower-level function. You’ll
typically want to use ivalues or values instead.
local function iter_values_of(next, state, i)
local iter = {next=next, state=state, i=i, is_exhausted=false}
return setmetatable(iter, _iter_values_of)
end
exports.iter_values_of = iter_values_ofIterate over the indexed values of a table. Returns a stateful iterator that yields single values.
Example:
local t = {1, 2, 3}
local x = ivalues(t)
for v in x do print(v) end
local function ivalues(t)
return iter_values_of(ipairs(t))
end
exports.ivalues = ivaluesIterate over the keyed values of a table. Returns a stateful iterator function that returns one value.
Example:
local t = {a=1, b=2, c=3}
local x = ivalues(t)
for v in x do print(v) end
local function values(t)
return iter_values_of(pairs(t))
end
exports.values = valuesCreate a stateful iterator of {k, v} pairs from a table.
Returns a stateful iterator function.
local function items(t)
local v
local next, state, k = pairs(t)
return function()
k, v = next(state, k)
return {k, v}
end
end
exports.items = itemsLike Lua’s built-in next() function, but works backwards over indexes,
from right-to-left.
local function prev(t, i)
if i then
i = i - 1
local v = t[i]
if v then
return i, v
end
end
end
exports.prev = prevIterate over ipairs in reverse.
Note this is a STATELESS iterator. Use rev_values if you want to iterate
over only values in reverse.
for i, v in rev_ipairs(t) do
print(v)
end
local function rev_ipairs(t)
return prev, t, (#t + 1)
end
exports.rev_ipairs = rev_ipairsA stateful iterator for reversed indexed values of table. Returns a stateful iterator.
local function rev_ivalues(t)
return iter_values_of(rev_ipairs(t))
end
exports.rev_ivalues = rev_ivaluesApply a filter function to all values of the iterator, returning a new
iterator containing only the items that passed the test.
predicate is a function that returns a boolean value. Anything it returns
true for is kept.
local function filter(predicate, next)
return function()
for v in next do
if predicate(v) then return v end
end
end
end
exports.filter = filterFilter a stateful iterator function, returning an iterator containing only
the items that fail the test. This function is the compliment of filter.
local function remove(predicate, next)
return function()
for v in next do
if not predicate(v) then return v end
end
end
end
exports.remove = removeMap each item with function a2b, returning a new iterator of mapped values.
Note that you may also use map to filter values, by returning nil.
This is useful when adhering to Lua’s convention of returning nil for
function exceptions. Failures are automatically filtered out.
This function can be used to serve a similar purpose to Python’s list
comprehensions and generator expressions. It lets you write your functions
for single values, then have them deal with any iterator sequence,
returning a new iterator sequence. Like Python’s list comprehensions, you
can both map and filter the values (to filter, simply return nil).
local function map(a2b, next)
return function()
for v in next do
local xv = a2b(v)Skip nil values, since nil terminates iteration in Lua.
if xv ~= nil then
return xv
end
end
end
end
exports.map = mapLift a function to become an iterator transformer.
Returns a new function that will consume an iterator and return a new
iterator, transformed with function a2b. Example:
function square(x) return x * x end
local squares = lift(square)
local t = {1, 2, 3}
local x = iter.ivalues(t)
local y = squares(x)
-- <1, 4, 9>
local function lift(a2b)
return function(x)
return map(a2b, x)
end
end
exports.lift = liftStep through iterator with a reducing function and a starting result value.
Returns an iterator for the result at each step of the reduction.
Example:
local v = iter.values({1, 2, 3, 4})
local function sum(x, y) return x + y end
local r = iter.reductions(sum, 0, v)
print(collect(r))
--- {1, 3, 6, 10}
local function reductions(step, result, next)
return function()
for v in next do
result = step(result, v)
return result
end
end
end
exports.reductions = reductionsTake the first n items of iterator.
Returns a new iterator that will terminate after yielding n items.
local function take(n, next)
return function()
for v in next do
n = n - 1
if n >= 0 then
return v
else
return nil
end
end
end
end
exports.take = takeTake items from iterator while predicate function returns true.
As soon as function returns false, stop iteration.
local function take_while(predicate, next)
return function()
for v in next do
if predicate(v) then
return v
else
return nil
end
end
end
end
exports.take_while = take_whileSkip the first n items of iterator.
Returns a new iterator that will yield items after skipping n items.
local function skip(n, next)
return function()
for v in next do
n = n - 1
if n < 0 then
return v
end
end
end
end
exports.skip = skipSkip items until predicate function returns true.
Returns a new iterator with items after predicate returns true.
local function skip_while(predicate, next)
local skipping = true
return function()
for v in next do
if skipping then
skipping = predicate(v)
else
return v
end
end
end
end
exports.skip_while = skip_whilePartition an iterator into “chunks”, returning an iterator of tables
containing chunk_size items each.
Returns a new iterator of chunks.
local function partition(chunk_size, next)
return function()
local chunk = {}
for v in next do
table.insert(chunk, v)
if #chunk == chunk_size then
return chunk
end
endIf we have any values in the last chunk, return it.
if #chunk > 0 then
return chunk
endOtherwise, return nothing
end
end
exports.partition = partitionZip 2 iterators using function f.
Each step, takes one item from the left and on item from the right iterator,
and passes these items to function f. The return value of f becomes the
value at that step of the returned iterator.
Terminates on shortest iterator.
Example:
zip_with(table2, iter_1, iter_2)
-- <{1, 2}, {3, 4}, {5, 6}, ...>
local function zip_with(f, next_a, next_b)
return function()
a = next_a()
b = next_b()
if a ~= nil and b ~= nil then
return f(a, b)
end
end
end
exports.zip_with = zip_withPack 2 arguments into a table.
local function table2(a, b)
return {a, b}
endZip 2 iterators into an iterator of tables made up of paired values.
local function zip(next_a, next_b)
return zip_with(table2, next_a, next_b)
end
exports.zip = zipRemove adjacent duplicates from iterator. Values are compared with compare
function. Function gets previous and current value. If it returns true, the
pair is not considered to be a duplicate.
local function dedupe_with(compare, next)
local prev = dedupe
return function()
for curr in next do
if compare(prev, curr) then
prev = curr
return curr
end
end
end
end
exports.dedupe_with = dedupe_with
local function different(x, y)
return x ~= y
endRemove adjacent duplicates from iterator.
local function dedupe(next)
return dedupe_with(different, next)
end
exports.dedupe = dedupeReduce over an iterator and produce a result.
local function reduce(step, result, next)
for v in next do
result = step(result, v)
if result == nil then
break
end
end
return result
end
exports.reduce = reduce
local function add(a, b)
return a + b
end
exports.add = addSum over all of the values of iterator next, starting with number start.
Example:
local t = {1, 2, 3}
local x = iter.ivalues(t)
local n = iter.sum(10, x)
-- 16
local function sum(start, next)
return reduce(add, start, next)
end
exports.sum = sum
local function append(t, v)
table.insert(t, v)
return t
end
exports.append = appendInsert values from iterator into table t.
Mutates and returns t.
local function extend(t, next)
return reduce(append, t, next)
end
exports.extend = extendCollect an iterator’s values into a table. Typical iterator workflow is to transform iterators, then collect the result. Example:
local x = iter.ivalues({1, 2, 3, 4, 5})
local chunks = partition(3, x)
local t = collect(chunks)
print(t) -- {{1, 2, 3}, {4, 5}}
local function collect(next)
return extend({}, next)
end
exports.collect = collect
local function compare_min(x, y)
if x and x < y then
return x
else
return y
end
endGet the smallest number in the iterator.
local function min(next, ...)
return reduce(compare_min, nil, next, ...)
end
exports.min = min
local function compare_max(x, y)
if x and x > y then
return x
else
return y
end
endGet the largest number in the iterator.
local function max(next, ...)
return reduce(compare_max, nil, next, ...)
end
exports.max = maxSearch through iterator next, and return the first value that passes
the predicate function.
local function find(predicate, next)
for v in next do
if predicate(v) then
return v
end
end
end
exports.find = find
return exports