Utility functions - Yonaba/Moses GitHub Wiki
local M = require 'moses'
The no-operation function. Takes nothing, returns nothing. It is being used internally.
M.noop() -- => nil
Returns the passed-in value.
This function is internally used as a default transformation function.
M.identity(1)-- => 1
M.identity(false) -- => false
M.identity('hello!') -- => 'hello!'
Calls f
with the supplied arguments. Returns the results of f(...)
.
M.call(math.pow, 2, 3) -- => 8
M.call(string.len, 'hello' ) -- => 5
M.call(table.concat, {1,2,3,4,5}, ',', 2, 4) -- => {2,3,4}
Creates a constant function. This function will continuously yield the same output.
local pi = M.constant(math.pi)
pi(1) -- => 3.1415926535898
pi(2) -- => 3.1415926535898
pi(math.pi) -- => 3.1415926535898
Returns a function which applies specs
on args. This function will produce an object having the same structure than specs
by mapping each property to the result of calling its associated function with the supplied arguments.
local stats = M.applySpec({
min = function(...) return math.min(...) end,
max = function(...) return math.max(...) end,
})
stats(5,4,10,1,8) -- => {min = 1, max = 10}
Threads value
through a series of functions.
local function inc(x) return x + 1 end
local function double(x) return 2 * x end
local function square(x) return x * x end
M.thread(2, inc, double, square) -- => 36
M.thread(3, double, inc, square) -- => 49
M.thread(4, square, double, inc) -- => 33
M.thread(5, square, inc, double) -- => 52
If a function expects more than one args, it can be specified using an array list, where the first item is the function and the following are the remaining args neeeded.
local function inc(x) return x + 1 end
local function add(x, y) return x * y end
local function pow(x, y) return x ^ y end
M.thread(2, inc, {add, 3}, {pow, 2}) -- => 36
M.thread(2, {add, 4}, inc, {pow, 2}) -- => 49
Threads value
through a series of functions. If a function expects more than one args,
it can be specified using an array list, where the first item is the function and the following are
the remaining args neeeded. The value is used as the last input.
local function inc(x) return x + 1 end
local function add(x, y) return x * y end
local function pow(x, y) return x ^ y end
M.threadRight(2, inc, {add, 3}, {pow, 2}) -- => 64
M.threadRight(2, {add, 4}, inc, {pow, 2}) -- => 128
Returns a dispatching function. When called with arguments, this function invokes each of its functions in the passed-in order and returns the results of the first non-nil evaluation.
local f = M.dispatch(
function() return nil end,
function (v) return v+1 end,
function (v) return 2*v end
)
f(5) -- => 6
f(7) -- => 8
Aliases: cache
.
Memoizes a slow-running function. It caches the result for a specific input, so that the next time the function is called with the same input, it will lookup the result in its cache, instead of running again the function body.
local function fibonacci(n)
return n < 2 and n or fibonacci(n-1)+fibonacci(n-2)
end
local mem_fibonacci = M.memoize(fibonacci)
fibonacci(20) -- => 6765 (but takes some time)
mem_fibonacci(20) -- => 6765 (takes less time)
Builds a list from a seed value. Accepts an iterator function, which returns either nil to stop iteration or two values : the value to add to the list of results and the seed to be used in the next call to the iterator function.
local function f(v)
if v < 100 then return v, v * 2 end
end
local t = M.unfold(f, 10) -- => {10,20,40,80}
Produces a function that runs only once. Successive calls to this function will still yield the same input.
local sq = M.once(function(a) return a*a end)
sq(1) -- => 1
sq(2) -- => 1
sq(3) -- => 1
sq(4) -- => 1
sq(5) -- => 1
Returns a version of f
that will run no more than count
times. Next calls will keep yielding the results of the (n-th)-1 call.
local function greet(someone) return 'hello '..someone end
local greetOnly3people = M.before(greet, 3)
greetOnly3people('John') -- => 'hello John'
greetOnly3people('Moe') -- => 'hello Moe'
greetOnly3people('James') -- => 'hello James'
greetOnly3people('Joseph') -- => 'hello James'
greetOnly3people('Allan') -- => 'hello James'
Produces a function that will respond only after a given number of calls.
local f = M.after(M.identity,3)
f(1) -- => nil
f(2) -- => nil
f(3) -- => 3
f(4) -- => 4
Composes functions. Each function consumes the return value of the one that follows.
local function f(x) return x^2 end
local function g(x) return x+1 end
local function h(x) return x/2 end
local compositae = M.compose(f,g,h)
compositae(10) -- => 36
compositae(20) -- => 121
Pipes a value through a series of functions.
local function f(x) return x^2 end
local function g(x) return x+1 end
local function h(x) return x/2 end
M.pipe(10,f,g,h) -- => 36
M.pipe(20,f,g,h) -- => 121
Returns a function which returns the logical complement of a given function.
M.complement(function() return true end)() -- => false
Aliases: juxt
.
Calls a sequence of functions with the same input.
local function f(x) return x^2 end
local function g(x) return x+1 end
local function h(x) return x/2 end
M.juxtapose(10, f, g, h) -- => 100, 11, 5
Wraps a function inside a wrapper. Allows the wrapper to execute code before and after function run.
local greet = function(name) return "hi: " .. name end
local greet_backwards = M.wrap(greet, function(f,arg)
return f(arg) ..'\nhi: ' .. arg:reverse()
end)
greet_backwards('John')
-- => hi: John
-- => hi: nhoJ
Calls a given function n
times.
local f = ('Lua programming'):gmatch('.')
M.times(f, 3) -- => {'L','u','a'}
Binds a value to be the first argument to a function.
local sqrt2 = M.bind(math.sqrt,2)
sqrt2() -- => 1.4142135623731
Binds a value to be the second argument to a function.
local last2 = M.bind(M.last,2)
last2({1,2,3,4,5,6}) -- => {5,6}
Binds a variable number of values to be the first arguments to a function.
local function out(...) return table.concat {...} end
local out = M.bindn(out,'OutPut',':',' ')
out(1,2,3) -- => OutPut: 123
out('a','b','c','d') -- => OutPut: abcd
Binds methods to object. As such, when calling any of these methods, they will receive object as a first argument.
local window = {
setPos = function(w,x,y) w.x, w.y = x, y end,
setName = function(w,name) w.name = name end,
getName = function(w) return w.name end,
}
window = M.bindall(window, 'setPos', 'setName', 'getName')
window.setPos(10,15)
print(window.x, window.y) -- => 10,15
window.setName('fooApp')
print(window.name) -- => 'fooApp'
print(window.getName()) -- => 'fooApp'
Returns a function which iterate over an array list of conditions. It invokes each predicate, passing it given values. It returns the value of the corresponding function of the first predicate to return a non-nil value
local multipleOf = M.cond({
{function(v) return v%2==0 end, function(v) return v..' is multiple of 2' end},
{function(v) return v%3==0 end, function(v) return v..' is multiple of 3' end},
{function(v) return v%5==0 end, function(v) return v..' is multiple of 5' end},
{function() return true end, function(v) return 'could not find an answer for '..v end}
})
for i = 15, 20 do
print(multipleOf(i))
end
-- => 15 is multiple of 3
-- => 16 is multiple of 2
-- => could not find an answer for 17
-- => 18 is multiple of 2
-- => could not find an answer for 19
-- => 20 is multiple of 2
Returns a validation function. Given a set of functions, the validation function
evaluates to true
only when all its funcs returns true
.
local f = M.both(
function(x) return x > 0 end,
function(x) return x < 10 end,
function(x) return x % 2 == 0 end
)
f(2) -- => true
f(8) -- => true
f(9) -- => false
Returns a validation function. Given a set of functions, the validation function
evaluates to true
when one of its funcs returns true
.
local f = M.either(
function(x) return x > 0 end,
function(x) return x % 2 == 0 end
)
f(0) -- => true
f(-3) -- => false
Returns a validation function. Given a set of functions, the validation function
evaluates to true
when neither of its funcs returns true
.
local f = M.neither(
function(x) return x > 10 end,
function(x) return x % 2 == 0 end
)
f(12) -- => false
f(8) -- => false
f(7) -- => true
Aliases: uid
.
Returns an unique integer ID.
M.uniqueId() -- => 1
Can handle string templates for formatted output.
M.uniqueId('ID%s') -- => 'ID2'
Or a function, for the same purpose.
local formatter = function(ID) return '$'..ID..'$' end
M.uniqueId(formatter) -- => '$ID1$'
Aliases: iter
.
Returns an iterator function which constinuously applies a function f
onto an input value
.
For example, let us go through the powers of two using iterator
.
local function po2(x) return x*2 end
local function iter_po2 = M.iterator(po2, 1)
iter_po2() -- => 2
iter_po2() -- => 4
iter_po2() -- => 8
if n
is supplied, it will run at maximum n
times.
local function po2(x) return x*2 end
local function iter_po2 = M.iterator(po2, 1, 3)
iter_po2() -- => 2
iter_po2() -- => 4
iter_po2() -- => 8
iter_po2() -- => nil
Consumes the first n
values of a iterator then returns it.
local w = "hello"
local char = string.gmatch(w,'.')
local iter = M.skip(char, 3)
for w in iter do print(w) end -- => 'l', 'o'
n
defaults to 1 when not given.
local w = "hello"
local char = string.gmatch(w,'.')
local iter = M.skip(char)
for w in iter do print(w) end -- => 'e', 'l', 'l', 'o'
Iterates a given iterator function and returns its values packed in an array.
local text = 'letters'
local chars = string.gmatch(text, '.')
M.tabulate(chars) -- => {'l','e','t','t','e','r','s'}
Returns the length of an iterator.
local text = 'letters'
local chars = string.gmatch(text, '.')
M.iterlen(chars) -- => 7
It consumes the iterator itself.
local text = 'lua'
local chars = string.gmatch(text, '.')
M.iterlen(chars) -- => 3
chars() -- => nil
Casts the passed-in value to an array containing the value itself.
M.castArray(true) -- => {true}
M.castArray(2) -- => {2}
It leaves the given value untouched in case it is already a table.
local t = {1}
print(M.castArray(t) == t) -- => true
Creates a function of f
with arguments flipped in reverse order.
local function f(...) return table.concat({...}) end
local flipped = M.flip(f)
flipped('a','b','c') -- => 'cba'
Returns a function that gets the nth argument.
local f = M.nthArg(3)
f('a','b','c') -- => 'c'
If n is negative, the nth argument from the end is returned.
local f = M.nthArg(-2)
f('a','b','c') -- => 'b'
Returns a function which accepts up to one argument. It ignores any additional arguments.
local f = M.unary(function (...) return ... end)
f('a') - ==> 'a'
f('a','b','c') -- => 'a'
Aliases: nAry
.
Returns a function which accepts up to n
args. It ignores any additional arguments.
local f = M.ary(function (...) return ... end, 2)
f(1,2) - ==> 1,2
f(1,2,3,4) -- => 1,2
If n
is not given, it defaults to 1
.
local f = M.unary(function (...) return ... end)
f('a','b','c') -- => 'a'
Returns a function with an arity of 0. The new function ignores any arguments passed to it.
local f = M.noarg(function (x) return x or 'default' end)
f(1) -- => 'default'
f(function() end, 3) -- => 'default'
Returns a function which runs with arguments arranged according to given indexes
.
local f = M.rearg(function (...) return ... end, {5,4,3,2,1})
f('a','b','c','d','e') -- => 'e','d','c','b','a'
Creates a function that invokes a set of transforms with the arguments it receives.
One can use use for example to get the tuple of min and max values from a set of values
local minmax = M.over(math.min, math.max)
minmax(5,10,12,4,3) -- => {3,12}
Creates a validation function. The returned function checks if all of the given predicates return truthy when invoked with the arguments it receives.
local function alleven(...)
for i, v in ipairs({...}) do
if v%2~=0 then return false end
end
return true
end
local function allpositive(...)
for i, v in ipairs({...}) do
if v < 0 then return false end
end
return true
end
local allok = M.overEvery(alleven, allpositive)
allok(2,4,-1,8) -- => false
allok(10,3,2,6) -- => false
allok(8,4,6,10) -- => true
Creates a validation function. The returned function checks if any of the given predicates return truthy when invoked with the arguments it receives.
local function alleven(...)
for i, v in ipairs({...}) do
if v%2~=0 then return false end
end
return true
end
local function allpositive(...)
for i, v in ipairs({...}) do
if v < 0 then return false end
end
return true
end
local anyok = M.overSome(alleven,allpositive)
anyok(2,4,-1,8) -- => false
anyok(10,3,2,6) -- => true
anyok(-1,-5,-3) -- => false
Creates a function that invokes f
with its arguments transformed
local function f(x, y) return x, y end
local function triple(x) retun x*3 end
local function square(x) retun x^2 end
local new_f = M.overArgs(f, triple, square)
new_f(1,2) -- => 3, 4
new_f(10,10) -- => 30, 100
In case the number of arguments is greater than the number of transforms, the remaining args will be left as-is.
local function f(x, y, z) return x, y, z end
local function triple(x) retun x*3 end
local function square(x) retun x^2 end
local new_f = M.overArgs(f, triple, square)
new_f(1,2,3) -- => 3, 4, 3
new_f(10,10,10) -- => 30, 100, 10
Converges two functions into one.
local function pow2(x) return x*x end
local function pow3(x) return x*x*x end
local function sum(a,b) return a+b end
local poly = M.converge(sum, pow2, pow3)
poly(5) -- => 150 (ie. 5*5 + 5*5*5)
Partially apply a function by filling in any number of its arguments.
local function diff(a, b) return a - b end
local diffFrom20 = M.partial(diff, 20) -- arg 'a' will be 20 by default
diffFrom20(5) -- => 15
The string '_'
can be used as a placeholder in the list of arguments to specify an argument that should not be pre-filled, but is rather left open to be supplied at call-time.
local function diff(a, b) return a - b end
local remove5 = M.partial(diff, '_', 5) -- arg 'a' will be given at call-time, but 'b' is set to 5
remove5(20) -- => 15
Like M.partial
, it partially applies a function by filling in any number of its arguments, but from the right.
local function concat(...) return table.concat({...},',') end
local concat_right = M.partialRight(concat,'a','b','c')
concat_right('d') -- => d,a,b,c
concat_right = M.partialRight(concat,'a','b')
concat_right('c','d') -- => c,d,a,b
concat_right = M.partialRight(concat,'a')
concat_right('b','c','d') -- => b,c,d,a
The string '_'
, as always, can be used as a placeholder in the list of arguments to specify an argument that should not be pre-filled, but is rather left open to be supplied at call-time.
In that case, the first args supplied at runtime will be used to fill the initial list of args while the remaining will be prepended.
local function concat(...) return table.concat({...},',') end
local concat_right = M.partialRight(concat,'a','_','c')
concat_right('d','b') -- => b,a,d,c
concat_right = M.partialRight(concat,'a','b','_')
concat_right('c','d') -- => d,a,b,c
concat_right = M.partialRight(concat,'_','a')
concat_right('b','c','d') -- => c,d,b,a
Curries a function. If the given function f
takes multiple arguments, it returns another version of f
that takes a single argument
(the first of the arguments to the original function) and returns a new function that takes the remainder of the arguments and returns the result.
local function sumOf3args(x,y,z) return x + y + z end
local curried_sumOf3args = M.curry(sumOf3args, 3)
sumOf3args(1)(2)(3)) -- => 6
sumOf3args(0)(6)(9)) -- => 15
n_args
defaults to 2.
local function product(x,y) return x * y end
local curried_product = M.curry(product)
curried_product(5)(4) -- => 20
curried_product(3)(-5) -- => -15
curried_product(0)(1) -- => 0
Returns the execution time of f (...)
in seconds and its results.
local function wait_count(n)
local i = 0
while i < n do i = i + 1 end
return i
end
local time, i = M.time(wait_count, 1e6) -- => 0.002 1000000
local time, i = M.time(wait_count, 1e7) -- => 0.018 10000000