Y Combinator
Created|Updated
|Word Count:1.2k|Reading Time:5mins
用 JS 从零推导 Y 组合子
前置知识
Lambda Calculus
如果你还不了解 Lambda 演算,可以先阅读 Good Math/Bad Math Lambda Calculus 的系列文章。
Lambda Calculus Syntax
- Function definition
- Identifier reference
- Function application
为了降低理解门槛,我在以下使用 JS 的匿名函数语法,注意这和标准的 Lambda Calculus Syntax 有一定区别。
Lambda Calculus Evaluation Rules
1 2 3 4
|
const f1 = (x) => x + x; const f2 = (y) => y + y;
|
1 2 3 4 5 6 7 8 9
|
const f1 = ((x) => x + 1)(3); const f2 = 3 + 1;
const f3 = ((x, y) => x(y))((z) => z * z, 3); const f4 = (x) => 3 * 3;
|
What’s Y Combinator
纯正的 lambda 表达式是没有命名的,因此无法简单地通过函数名调用自身进行递归。
Y 组合子类似一个高阶函数,通过参数名来引用函数,进而巧妙地在不命名的情况下实现递归。
这次的目的就是推导这个高阶函数。
λ Y
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
| fact = (n) => (n === 1 ? 1 : n * fact(n - 1)); fact(5);
fact = (f, n) => (n === 1 ? 1 : n * f(f, n - 1)); fact(fact, 5);
((f, n) => (n === 1 ? 1 : n * f(f, n - 1)))( (f, n) => (n === 1 ? 1 : n * f(f, n - 1)), 5 );
fact = (f) => (n) => (n === 1 ? 1 : n * f(f)(n - 1)); fact(fact)(5);
((f) => (n) => (n === 1 ? 1 : n * f(f)(n - 1)))( (f) => (n) => (n === 1 ? 1 : n * f(f)(n - 1)) )(5);
w = (f) => f(f); w((f) => (n) => (n === 1 ? 1 : n * f(f)(n - 1)))(5);
((f) => f(f))((f) => (n) => (n === 1 ? 1 : n * f(f)(n - 1)))(5);
(f) => (n) => (n === 1 ? 1 : n * f(f)(n - 1)); g = f(f); (f) => ((g) => (n) => (n === 1 ? 1 : n * g(n - 1)))(f(f));
(f) => ((g) => (n) => (n === 1 ? 1 : n * g(n - 1)))((v) => f(f)(v));
fact0 = (f) => (n) => (n === 1 ? 1 : n * f(n - 1));
( (fact0) => (f) => fact0((v) => f(f)(v)) )((g) => (n) => (n === 1 ? 1 : n * g(n - 1)));
w( ( (fact0) => (f) => fact0((v) => f(f)(v)) )((g) => (n) => (n === 1 ? 1 : n * g(n - 1))) )(5);
((f) => f(f))( ( (fact0) => (f) => fact0((v) => f(f)(v)) )((g) => (n) => (n === 1 ? 1 : n * g(n - 1))) )(5);
(h) => ((f) => f(f))( ( (fact0) => (f) => fact0((v) => f(f)(v)) )(h) );
((h) => ((f) => f(f))( ( (fact0) => (f) => fact0((v) => f(f)(v)) )(h) ))((g) => (n) => (n === 1 ? 1 : n * g(n - 1)))(5);
Y = (h) => ((f) => f(f))( ( (s) => (f) => s((v) => f(f)(v)) )(h) );
Y = (h) => ((f) => f(f))((f) => h((v) => f(f)(v)));
Y = (h) => ((f) => h((v) => f(f)(v)))((f) => h((v) => f(f)(v)));
fact = (f) => (n) => (n === 1 ? 1 : n * f(n - 1));
factorial = Y(fact); factorial(5);
|
完成
最终我们就实现了一个帮助函数实现递归的高阶函数
1 2
| const Y = (h) => ((f) => h((v) => f(f)(v)))((f) => h((v) => f(f)(v)));
|
Bibliography