luaiter is a rewritten version of luafun: a high-performance functional programming library for Lua designed with LuaJIT's trace compiler in mind. luaiter focus plain Lua performance improve and follows the standard Lua iteration protocol.
luaiter has the same License as Lua itself.
Some improves:
- avoid any memory allocation when iteration.
- use standard iteration protocol.
- support Lua 5.3 bit operators.
- add more useful functions like
scanandflatmap. - add a powerful
selectorinterface for quick-and-dirty lambda function support.
luafun library use a custom protocol for iteration, makes using other
Lua-spec iterator e.g. io.lines(), string.gmatch difficult, it
requires a iteration state variable. luaiter follows the standard
protocol without the per-iteration state variable:
for var1, ... in iter, state, init do
...
endThe first return value of iter function var1 used as the state
variable, but it's meaningful: If iter function is stateful, i.e.
each iteration will change the state content, then var1 may occurs
the duplicate value in iterations. In this case, init will be nil
to indicate the beginning of the iteration (note that nil will never
occurs in iteration: it means the end of stream). Otherwise, the
iter will be stateless, means var1 will never repetition during
iteration.
- The stateful iterator example:
map(remember the original iteratorvar1). - The stateless iterator example:
range(it only use previousvar1to detect the nextvar1).
luaiter has a very special selector interface, the underscore
iter._. This is a special object that has several functinal:
-
_[1]..._[9]calledselector, they can be used as function that select it's 1st...9th argument, e.g._[5]same asfunction(a, b, c, d, e) return e end. They could shorten asiter._1toiter._9 -
_1to_9could used in expression, in this case the expression will return a function that do the calculation, and _1 ... _9 means the order of arguments, e.g._3 + _1 * _2same asfunction(a, b, c) return c + a * b end. This will support all Lua operator that could override by metatable, including_1[_2]. -
if use
_as a function, it could return a function that call the_'s single argument, e.g._(print)(_2, _1)same asfunction(a, b) return print(b, a) end, all underscore expression could be used in all place in call, e.g._(_2.each)(_3, _1*_4)same asfunction(a, b, c ,d) return b.each(c, a*d) end -
_.selfreturns a table-object, use_.self(obj).each(_1, _2)same asfunction(a, b) return obj:each(a, b) end. -
_.dotssame as..., if use_.dotsin a expression/call, the generated function will accept vararg arguments. -
_.land,_.lor,_.lnot,_.andorsame asand,or,notoperator anda and b or cexpression. -
used of
_and_1to_9may causeload/loadstringwhen first call the generated function, every time the underscore expression calculated, a new function willload/loadstringfrom expression, so don't write expression in loop. Generate the function, and store it in the iterator will cache the generated function.
A example:
> -- Functional style
> print(reduce(_1+_2, 0, map(_1^2, range(100))))
338350.0
> -- Object-oriented style
> print(range(100):map(_1^2):reduce(_1+_2))
338350.0All functions that accept a iterator may used as the method of
iterator object. Iterator usually place at the end of interface,
when used as methods, the last iterator will be self, e.g. map
function has signature: map(func, iter), So use map as a method
can call like this: iter:map(func)
If a function accept multiple iterators, the first will be the self
iterator, e.g. zip(iter, iters...) maybe called as iter:zip(iters...)
If a function doesn't accept a iterator, it can not used as the
method of iterator object.
Generators:
range([[first,] last[, step]])rand([first, last])str(string)array(table)resolve(...)dup(...)zeros() == dup(0)ones() == dup(1)
Slicing:
take(n, iter)drop(n, iter)slice(first, last, iter)
Transforms:
enumerate(iter)map(func, iter)flatmap(func, iter)scan(func, init, iter)group(n, iter)groupby(func, iter)
Compositions:
zip(iters...)interleave(iters...)chain(iters...)cycle(iter)
Filtering:
takewhile(func, iter)dropwhile(func, iter)filter(func, iter)fitlerout(func, iter)
Reducing:
each(func, iter)reduce(func, iter)index(func, iter)collect(t, iter)concat(delim, iter)count(iter)isempty(iter)all(func, iter)any(func, iter)