337 lines
7 KiB
Lua
337 lines
7 KiB
Lua
/*---------------------------------------------------------------------------
|
|
Functional library
|
|
|
|
by FPtje Atheos
|
|
---------------------------------------------------------------------------*/
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Function currying
|
|
Take a function with n parameters.
|
|
Currying is the procedure of storing k < n parameters "in the function"
|
|
in such a way that the remaining function can be called with n - k parameters
|
|
|
|
Example:
|
|
DebugPrint = fp{print, "[DEBUG]"}
|
|
DebugPrint("TEST")
|
|
> [DEBUG] TEST
|
|
---------------------------------------------------------------------------*/
|
|
function fp(tbl)
|
|
local func = tbl[1]
|
|
|
|
return function(...)
|
|
local fnArgs = {}
|
|
local arg = {...}
|
|
local tblN = table.maxn(tbl)
|
|
|
|
for i = 2, tblN do fnArgs[i - 1] = tbl[i] end
|
|
for i = 1, table.maxn(arg) do fnArgs[tblN + i - 1] = arg[i] end
|
|
|
|
return func(unpack(fnArgs, 1, table.maxn(fnArgs)))
|
|
end
|
|
end
|
|
|
|
local unpack = unpack
|
|
local table = table
|
|
local pairs = pairs
|
|
local ipairs = ipairs
|
|
local error = error
|
|
local math = math
|
|
local select = select
|
|
local type = type
|
|
local _G = _G
|
|
local fp = fp
|
|
|
|
|
|
module("fn")
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Parameter manipulation
|
|
---------------------------------------------------------------------------*/
|
|
Id = function(...) return ... end
|
|
|
|
Flip = function(f)
|
|
if not f then error("not a function") end
|
|
return function(b, a, ...)
|
|
return f(a, b, ...)
|
|
end
|
|
end
|
|
|
|
-- Definition from http://lua-users.org/wiki/CurriedLua
|
|
ReverseArgs = function(...)
|
|
|
|
--reverse args by building a function to do it, similar to the unpack() example
|
|
local function reverse_h(acc, v, ...)
|
|
if select('#', ...) == 0 then
|
|
return v, acc()
|
|
else
|
|
return reverse_h(function () return v, acc() end, ...)
|
|
end
|
|
end
|
|
|
|
-- initial acc is the end of the list
|
|
return reverse_h(function () return end, ...)
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Misc functions
|
|
---------------------------------------------------------------------------*/
|
|
-- function composition
|
|
-- function composition
|
|
do
|
|
local function comp_h(a, b, ...)
|
|
if b == nil then return a end
|
|
b = comp_h(b, ...)
|
|
return function(...)
|
|
return a(b(...))
|
|
end
|
|
end
|
|
Compose = function(funcs, ...)
|
|
if type(funcs) == 'table' then
|
|
return comp_h(unpack(funcs))
|
|
else
|
|
return comp_h(funcs, ...)
|
|
end
|
|
end
|
|
end
|
|
|
|
_G.fc = Compose
|
|
|
|
-- Definition from http://lua-users.org/wiki/CurriedLua
|
|
Curry = function(func, num_args)
|
|
if not num_args then error("Missing argument #2: num_args") end
|
|
if not func then error("Function does not exist!", 2) end
|
|
-- helper
|
|
local function curry_h(argtrace, n)
|
|
if n == 0 then
|
|
-- reverse argument list and call function
|
|
return func(ReverseArgs(argtrace()))
|
|
else
|
|
-- "push" argument (by building a wrapper function) and decrement n
|
|
return function(x)
|
|
return curry_h(function() return x, argtrace() end, n - 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- no sense currying for 1 arg or less
|
|
if num_args > 1 then
|
|
return curry_h(function() return end, num_args)
|
|
else
|
|
return func
|
|
end
|
|
end
|
|
|
|
-- Thanks Lexic!
|
|
Partial = function(func, ...)
|
|
local args = {...}
|
|
return function(...)
|
|
return func(unpack(table.Add( args, {...})))
|
|
end
|
|
end
|
|
|
|
Apply = function(f, ...) return f(...) end
|
|
|
|
Const = function(a, b) return a end
|
|
Until = function(cmp, fn, val)
|
|
if cmp(val) then
|
|
return val
|
|
end
|
|
return Until(cmp, fn, fn(val))
|
|
end
|
|
|
|
Seq = function(f, x) f(x) return x end
|
|
|
|
GetGlobalVar = function(key) return _G[key] end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Mathematical operators and functions
|
|
---------------------------------------------------------------------------*/
|
|
Add = function(a, b) return a + b end
|
|
Sub = function(a, b) return a - b end
|
|
Mul = function(a, b) return a * b end
|
|
Div = function(a, b) return a / b end
|
|
Mod = function(a, b) return a % b end
|
|
Neg = function(a) return -a end
|
|
|
|
Eq = function(a, b) return a == b end
|
|
Neq = function(a, b) return a ~= b end
|
|
Gt = function(a, b) return a > b end
|
|
Lt = function(a, b) return a < b end
|
|
Gte = function(a, b) return a >= b end
|
|
Lte = function(a, b) return a <= b end
|
|
|
|
Succ = Compose{Add, 1}
|
|
Pred = Compose{Flip(Sub), 1}
|
|
Even = Compose{fp{Eq, 0}, fp{Flip(Mod), 2}}
|
|
Odd = Compose{Not, Even}
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Functional logical operators and conditions
|
|
---------------------------------------------------------------------------*/
|
|
FAnd = function(fns)
|
|
return function(...)
|
|
local val
|
|
for _, f in pairs(fns) do
|
|
val = {f(...)}
|
|
if not val[1] then return unpack(val) end
|
|
end
|
|
if val then return unpack(val) end
|
|
end
|
|
end
|
|
|
|
FOr = function(fns)
|
|
return function(...)
|
|
for _, f in pairs(fns) do
|
|
local val = {f(...)}
|
|
if val[1] then return unpack(val) end
|
|
end
|
|
return false
|
|
end
|
|
end
|
|
|
|
Not = function(x) return not x end
|
|
|
|
If = function(f, Then, Else)
|
|
return function(x)
|
|
if f(x) then
|
|
return Then
|
|
else
|
|
return Else
|
|
end
|
|
end
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
List operations
|
|
---------------------------------------------------------------------------*/
|
|
Map = function(f, xs)
|
|
for k, v in pairs(xs) do
|
|
xs[k] = f(v)
|
|
end
|
|
return xs
|
|
end
|
|
|
|
Append = function(xs, ys)
|
|
return table.Add(xs, ys)
|
|
end
|
|
|
|
Filter = function(f, xs)
|
|
local res = {}
|
|
for k,v in pairs(xs) do
|
|
if f(v) then res[k] = v end
|
|
end
|
|
return res
|
|
end
|
|
|
|
ForEach = function(f, xs)
|
|
for k,v in pairs(xs) do
|
|
local val = f(k, v)
|
|
if val ~= nil then return val end
|
|
end
|
|
end
|
|
|
|
Head = function(xs)
|
|
return table.GetFirstValue(xs)
|
|
end
|
|
|
|
Last = function(xs)
|
|
return xs[#xs] or table.GetLastValue(xs)
|
|
end
|
|
|
|
Tail = function(xs)
|
|
table.remove(xs, 1)
|
|
return xs
|
|
end
|
|
|
|
Init = function(xs)
|
|
xs[#xs] = nil
|
|
return xs
|
|
end
|
|
|
|
GetValue = function(i, xs)
|
|
return xs[i]
|
|
end
|
|
|
|
Null = function(xs)
|
|
for k, v in pairs(xs) do
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
Length = function(xs)
|
|
return #xs
|
|
end
|
|
|
|
Index = function(xs, i)
|
|
return xs[i]
|
|
end
|
|
|
|
Reverse = function(xs)
|
|
local res = {}
|
|
for i = #xs, 1, -1 do
|
|
res[#xs - i + 1] = xs[i]
|
|
end
|
|
return res
|
|
end
|
|
|
|
/*---------------------------------------------------------------------------
|
|
Folds
|
|
---------------------------------------------------------------------------*/
|
|
Foldr = function(func, val, xs)
|
|
for i = #xs, 1, -1 do
|
|
val = func(xs[i], val)
|
|
end
|
|
|
|
return val
|
|
end
|
|
|
|
Foldl = function(func, val, xs)
|
|
for k, v in ipairs(xs) do
|
|
val = func(val, v)
|
|
end
|
|
|
|
return val
|
|
end
|
|
|
|
And = function(xs)
|
|
for k, v in pairs(xs) do
|
|
if v ~= true then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
Or = function(xs)
|
|
for k, v in pairs(xs) do
|
|
if v == true then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
Any = function(func, xs)
|
|
for k, v in pairs(xs) do
|
|
if func(v) == true then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
All = function(func, xs)
|
|
for k, v in pairs(xs) do
|
|
if func(v) ~= true then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
Sum = _G.fp{Foldr, Add, 0}
|
|
|
|
Product = _G.fp{Foldr, Mul, 1}
|
|
|
|
Concat = _G.fp{Foldr, Append, {}}
|
|
|
|
Maximum = _G.fp{Foldl, math.Max, -math.huge}
|
|
|
|
Minimum = _G.fp{Foldl, math.Min, math.huge}
|
|
|
|
Snd = _G.fp{select, 2}
|
|
|
|
Thrd = _G.fp{select, 3}
|