Module:Iterator

local function vararg_any(...) return select('#', ...) ~= 0 end

local function vararg_if(some, none, ...) if vararg_any(...) then return some(...) elseif none then return none end end

local function identity(...) return ... end

local iterfuncs = {} local wrap local mt = { __index = function(t, k)		return function(_, ...) return iterfuncs[k](t.iter, ...) end end, __call = function(t) return t.iter end, __tostring = function(t) return " " end }

do -- call f for each item in iter iterfuncs.each = function(iter, f)		repeat local done = false vararg_if(f, function done = true	end, iter) until done end

-- maps item... to f(item)... iterfuncs.map = function(iter, f)		return wrap(function			return vararg_if(f, nil, iter)		end) end

-- call f(item from it1, item from it2) for each iterfuncs.zip = function(iter, iter2, f)		return wrap(function			return vararg_if(function(...) local n = select('#', ...) return vararg_if(function(...)					if select('#', ...) > n then						return ...					end				end, nil, ..., iter2) end, nil, iter)		end) end

-- keeps only items where f(item) is truthy iterfuncs.filter = function(iter, pred) pred = pred or identity local inner inner = function return vararg_if(function(...)				if pred(...) then					return ...				else					return inner				end			end, nil, iter) end return wrap(inner) end

-- returns true if pred(item) is true for any item iterfuncs.any = function(iter, pred) pred = pred or identity repeat local done = false local ok = vararg_if(pred, function done = true end, iter) if ok then return true end until done return false end

-- returns true if pred(item) is true for all items iterfuncs.all = function(iter, pred) pred = pred or identity return not iterfuncs.any(iter, function(...) return not pred(...) end) end

-- converts a 1-iterator to a list iterfuncs.to_list = function(iter) local res = {} iterfuncs.each(iter, function(...)			local arity = select('#', ...)			if arity ~= 1 then				error(("Expected 1-iterator, got %d-iterator"):format(arity))			end			local x = ...			res[#res+1] = x		end) return res end

-- converts a 2-iterator to a dict iterfuncs.to_dict = function(iter) local res = {} iterfuncs.each(iter, function(...)			local arity = select('#', ...)			if arity ~= 2 then				error(("Expected 2-iterator, got %d-iterator"):format(arity))			end			local k, v = ...			res[k] = v		end) return res end

-- joins a 1-iterator iterfuncs.join = function(iter, val) return table.concat(iterfuncs.to_list(iter), val) end end

local funcs = {} funcs.range = function(n) local i = 1 return wrap(function		if i < n then			local cur = i			i = i + 1			return cur		else			return		end	end) end funcs.pairs = function(t) local next = pairs(t) -- fix for scribuntu local k, v = next(t, k)	return wrap(function		if k then			local curk, curv = k, v			k, v = next(t, k)			return curk, curv		end	end) end funcs.values = function(t) return iterfuncs.map(funcs.pairs(t), function(k, v) return v end) end funcs.keys = function(t) return iterfuncs.map(funcs.pairs(t), function(k, v) return k end) end

-- syntactic sugar - adds method to an iterator wrap = function(iter) if type(iter) == 'function' then return setmetatable({iter=iter}, mt) elseif getmetatable(iter) == mt then return iter elseif type(iter) == 'table' then return funcs.values(iter) end end

setmetatable(funcs, {	__call = function(t, iter) return wrap(iter) end })

return funcs