Recursion 递归
const first = xs => xs[0]
const rest = xs => xs.slice(1)
sum
const sum = xs => (xs.length === 0 ? 0 : first(xs) + sum(rest(xs)))
reverse
const reverse = xs => (xs.length === 0 ? [] : reverse(rest(xs)).concat(first(xs)))
tail recursive 尾递归
const sum = list => {
const go = (acc, xs) => (xs.length === 0 ? acc : go(acc + first(xs), rest(xs)))
return go(0, list)
}
const reverse = list => {
const go = (acc, xs) => (xs.length === 0 ? acc : go([first(xs)].concat(acc), rest(xs)))
return go([], list)
}
reduce
const reduce = (f, acc, xs) => (xs.length === 0 ? acc : reduce(f, f(acc, first(xs)), rest(xs)))
Tail call optimization is not supported by Chrome, you can use a loop to compromise.
const reduce = function (f, acc, iterable) {
for (let i of iterable) {
acc = f(acc, i)
}
return acc
}
Higher-order functions 高阶函数
sum
const sum = xs => reduce((acc, x) => acc + x, 0, xs)
reverse
const reverse = xs => reduce((acc, x) => [x].concat(acc), [], xs)
map
const map = (f, xs) => reduce((acc, x) => acc.concat(f(x)), [], xs)
filter
const filter = (f, xs) => reduce((acc, x) => (f(x) ? acc.concat(x) : acc), [], xs)
all
const all = xs => reduce((acc, x) => acc && x, true, xs)
any
const any = xs => reduce((acc, x) => acc || x, false, xs)
paramorphism 参数形式
const para = (f, acc, xs) => (xs.length === 0 ? acc : para(f, f(acc, first(xs), xs), rest(xs)))
Corecursion 协程递归
unfold, range
const unfold = (f, seed) => {
const go = (f, seed, acc) => {
const res = f(seed)
return res ? go(f, res[1], acc.concat([res[0]])) : acc
}
return go(f, seed, [])
}
const range = (i, count) => unfold(x => (x <= count ? [x, x + 1] : null), i)
range(5, 10) // [5, 6, 7, 8, 9, 10]
linked list
const Nil = {}
const _Cons = function (h, tl) {
this.head = h
this.tail = tl
}
const Cons = (h, tl) => new _Cons(h, tl)
const fold = (f, acc, xs) => (xs.head ? fold(f, f(acc, xs.head), xs.tail) : acc)
const lst = Cons(3, Cons(4, Cons(5, Nil)))
fold((acc, x) => acc + x, 0, lst) // 12
Tree
const Empty = {}
const _Leaf = function (x) {
this.x = x
}
const Leaf = x => new _Leaf(x)
const _Node = function (l, x, r) {
this.left = l
this.x = x
this.right = r
}
const Node = (l, x, r) => new _Node(l, x, r)
// Try to implement fold yourself
const tree = Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
fold((acc, x) => acc + x, 0, tree) // 10
Transducers 转换器
const concat = (a,b) = a.concat(b)
map
const mapper = (f, cnct) => (acc, x) => cnct(acc, f(x))
reduce(
mapper(x => x + 1, concat),
[],
[1, 2, 3]
)
// [2, 3, 4]
filter
const filterer = (f, cnct) => (acc, x) => f(x) ? cnct(acc, x) : acc
reduce(
filterer(x => x > 1, concat),
[],
[1, 2, 3]
)
// [2, 3]
filter and map
reduce(
filterer(
x => x > 1,
mapper(x => x + 1, concat)
),
[],
[1, 2, 3]
)
// [3, 4]
// Try to implement append yourself
reduce(
filterer(
x => x > 1,
mapper(x => x + 1, append)
),
Nil,
Cons(1, Cons(2, Cons(3, Nil)))
)
// [3,4]
// Try to implement insert yourself
reduce(
filterer(
x => x > 1,
mapper(x => x + 1, insert)
),
Empty,
Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
)
// [3,4]
Monoids 幺半群
const fold = xs => (xs.length ? first(xs).concat(fold(rest(xs))) : empty)
sum
const _Sum = function (x) {
this.val = x
}
const Sum = x => new _Sum(x)
_Sum.prototype.concat = y => Sum(this.val + y.val)
_Sum.prototype.empty = () => Sum(0)
const empty = _Sum.prototype.empty()
fold([Sum(1), Sum(2), Sum(3), Sum(4)]) // Sum(10)
product
const _Product = function (x) {
this.val = x
}
const Product = x => new _Product(x)
_Product.prototype.concat = y => Product(this.val * y.val)
_Product.prototype.empty = () => Product(1)
const empty = _Product.prototype.empty()
fold([Product(1), Product(2), Product(3), Product(4)]) // Product(24)
max
const _Max = function (x) {
this.val = x
}
const Max = x => new _Max(x)
_Max.prototype.concat = y => Max(this.val > y.val ? this.val : y.val)
_Max.prototype.empty = () => Max(-Infinity)
const empty = _Max.prototype.empty()
fold([Max(1), Max(2), Max(3), Max(4)]) // Max(4)
all
const _All = function (x) {
this.val = x
}
const All = x => new _All(x)
_All.prototype.concat = y => All(this.val && y.val)
_All.prototype.empty = () => All(true)
const empty = _All.prototype.empty()
fold([All(false), All(false), All(true), All(false)]) // All(false)
any
const _Any = function (x) {
this.val = x
}
const Any = x => new _Any(x)
_Any.prototype.concat = y => Any(this.val || y.val)
_Any.prototype.empty = () => Any(false)
const empty = _Any.prototype.empty()
fold([Any(false), Any(false), Any(true), Any(false)]) // Any(true)
F-Algebras F-代数
catamorphism 折叠
const cata = (f, xs) => f(xs.map(ys => cata(f, ys)))
sum
Nil.map = f => Nil
_Cons.prototype.map = function (f) {
return Cons(f(this.head), this.tail.map(f))
}
const sum = x => (x === Nil ? 0 : x.head + x.tail)
const lst = Cons(2, Cons(3, Cons(4, Nil)))
cata(sum, lst) // 9
map
const map = (f, xs) => cata(x => (x == Nil ? Nil : Cons(f(x.head), x.tail)), xs)
map(x => x + 1, Cons(2, Cons(3, Cons(4, Nil)))) // Cons(3, Cons(4, Cons(5, Nil)))
Empty.map = f => Empty
_Leaf.prototype.map = function (f) {
return Leaf(this.x)
}
_Node.prototype.map = function (f) {
return Node(f(this.left), this.x, f(this.right))
}
const tr = Node(Node(Leaf(2), 1, Leaf(3)), 0, Leaf(4))
cata(t => (t.constructor === _Node ? t.left + t.x + t.right : t.constructor === _Leaf ? t.x : 0), tr)
// 10
anamorphism 逆折叠
const ana = (g, a) => g(a).map(x => ana(g, x))
arrToList
const arrToList = xs => (xs.length === 0 ? Nil : Cons(first(xs), rest(xs)))
makeAlphabet
const makeAlphabet = x => (x > 25 ? Nil : Cons(String.fromCharCode(x + 65), x + 1))
range
const range = (acc, count) => ana(x => (x >= count ? Nil : Cons(x, x + 1)), acc)
read-world examples
const _Const = function (val) {
this.val = val
}
const Const = x => new _Const(x)
const _Add = function (x, y) {
this.x = x
this.y = y
}
const Add = (x, y) => new _Add(x, y)
const _Mul = function (x, y) {
this.x = x
this.y = y
}
const Mul = (x, y) => new _Mul(x, y)
_Const.prototype.map = function (f) {
return this
}
_Add.prototype.map = function (f) {
return Add(f(this.x), f(this.y))
}
_Mul.prototype.map = function (f) {
return Mul(f(this.x), f(this.y))
}
const interpret = a => (a.constructor === _Mul ? a.x * a.y : a.constructor === _Add ? a.x + a.y : /* a.constructor === _Const */ a.val)
const program = Mul(Add(Const(2), Const(3)), Const(4))
cata(interpret, program)
//=> 20
const _Concat = function (v, next) {
this.val = v
this.next = next
}
const Concat = (v, x) => new _Concat(v, x)
const _Replace = function (v, x, next) {
this.val = v
this.x = x
this.next = next
}
const Replace = (v, x, nt) => new _Replace(v, x, nt)
const _Input = function (v) {
this.val = v
}
const Input = v => new _Input(v)
_Concat.prototype.map = function (f) {
return Concat(this.val, f(this.next))
}
_Replace.prototype.map = function (f) {
return Replace(this.val, this.x, f(this.next))
}
_Input.prototype.map = function (f) {
return Input(this.val)
}
const interpret = t => (t.constructor === _Concat ? t.next.concat(t.val) : t.constructor === _Replace ? t.next.replace(t.val, t.x) : /* t.constructor === _Input */ t.val)
const prog = Concat('world', Replace('h', 'm', Input('hello')))
cata(interpret, prog)
//=> melloworld
const interpret1 = t =>
t.constructor === _Concat
? 'concatting ' + t.val + ' after ' + t.next
: t.constructor === _Replace
? 'replacing ' + t.val + ' with ' + t.x + ' on ' + t.next
: /* t.constructor === _Input */ t.val
const prog = Concat('world', Replace('h', 'm', Input('hello')))
cata(interpret1, prog)
//=> concatting world after replacing h with m on hello