阅读笔记 - 你可能并不需要使用循环

82 阅读3分钟

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