Skip to content

Instantly share code, notes, and snippets.

@Kaleidosium
Last active March 28, 2025 04:11
Show Gist options
  • Save Kaleidosium/1af5454e7050c48db35e3ced8d7fac31 to your computer and use it in GitHub Desktop.
Save Kaleidosium/1af5454e7050c48db35e3ced8d7fac31 to your computer and use it in GitHub Desktop.
Functional Programming utilities for YueScript
-- https://stackoverflow.com/a/11671820
-- e.g: map({1,2,3,4}, (o) -> o * 2) -> {2,4,6,8}
export map = (tbl, fn) ->
out = {}
for k, v in pairs tbl
out[k] = fn v
return out
-- https://gist.github.com/FGRibreau/3790217
-- e.g: filter({"a","b","c","d"}, (o, k, i) -> o >= "c") -> { [3]: "c", [4]: "d" }
export filter = (tbl, fn) ->
out = {}
for k, v in pairs tbl
if fn v, k, tbl then out[k] = v
return out
-- Left fold (processes list from left to right)
-- e.g: foldl({1,2,3,4}, 0, (acc, x) -> acc + x) -> 10
export foldl = (tbl, initial, fn) ->
return initial if not tbl or #tbl == 0
acc = initial
for i = 1, #tbl
acc = fn acc, tbl[i]
return acc
-- Right fold (processes list from right to left)
-- e.g: foldr({1,2,3,4,5}, 1, (acc, x) -> acc * x) -> 120
export foldr = (tbl, initial, fn) ->
return initial if not tbl or #tbl == 0
acc = initial
for i = #tbl, 1, -1
acc = fn acc, tbl[i]
return acc
-- reduce is an alias for foldr
export reduce = (tbl, init, fn) ->
return foldr(tbl, init, fn)
-- foldl1/foldr1 take the first/last element as the initial value --
-- e.g: foldl1({1,2,3,4,5}, (acc, x) -> if acc > x then acc else x) -> 5
export foldl1 = (tbl, fn) ->
return nil if not tbl or #tbl == 0
return tbl[1] if #tbl == 1
rest = [tbl[i] for i = 2, #tbl]
return foldl rest, tbl[1], fn
-- e.g: foldr1({1,2,3,4,5}, (acc, x) -> if acc < x then acc else x) -> 1
export foldr1 = (tbl, fn) ->
return nil if not tbl or #tbl == 0
return tbl[#tbl] if #tbl == 1
init = tbl[#tbl]
rest = [tbl[i] for i = 1, #tbl - 1]
return foldr rest, init, fn
-- e.g: printf = compose(io.write, string.format)
-- -> function(...) return io.write(string.format(unpack(arg))) end
export compose = (f, g) ->
return (...) -> f(g(...))
-- https://lua-stdlib.github.io/functional/modules/functional.html#bind
-- Partially apply a function with arguments at specified positions
-- arg_t is a table where keys are argument positions and values are the bound values
-- e.g: cube = bind(math.pow, {[2]: 3}) -- cube(2) returns 2^3 = 8
export bind = (fn, arg_t) ->
return (...) ->
args = {...}
-- Create a copy of the bound arguments
bound_args = {}
for k, v in pairs arg_t
bound_args[k] = v
-- Fill in the unbound arguments
arg_index = 1
for i = 1, math.max(#args, table.maxn(bound_args))
if not bound_args[i]
bound_args[i] = args[arg_index]
arg_index += 1
return fn(unpack(bound_args))
-- https://lua-stdlib.github.io/functional/modules/functional.html#curry
-- Curry a function with the specified number of arguments
export curry = (fn, n) ->
if n == 0 then return fn
return (x) ->
return curry(bind(fn, {x}), n - 1)
-- Ruby-style zip
-- e.g: zip({1,2,3}, {"a","b","c"}) -> {{1,"a"}, {2,"b"}, {3,"c"}}
export zip = (a, b) ->
result = {}
for i = 1, math.min(#a, #b)
result[i] = {a[i], b[i]}
return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment