写在前面

这是廖雪峰老师的博客出现的不计其数的例子, 原来看了没懂, 最近补上了一些基础知识, 试着再理解一下.

廖老师的博客原文在这, 如果还不知道廖老师, 那么非常推荐你去看看.

阿隆佐邱奇的脑洞

很久很久以前,有个叫阿隆佐·邱奇的帅哥,发现只需要用函数,就可以用计算机实现运算,而不需要0123这些数字和+-*/这些符号。

JavaScript支持函数,所以可以用JavaScript用函数来写这些计算。来试试:

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
'use strict';

// 定义数字0:
var zero = function (f) {
return function (x) {
return x;
}
};

// 定义数字1:
var one = function (f) {
return function (x) {
return f(x);
}
};

// 定义加法:
function add(n, m) {
return function (f) {
return function (x) {
return m(f)(n(f)(x));
}
}
}

// 计算数字2 = 1 + 1:
var two = add(one, one);

// 计算数字3 = 1 + 2:
var three = add(one, two);

// 计算数字5 = 2 + 3:
var five = add(two, three);

// 你说它是3就是3,你说它是5就是5,你怎么证明?

// 呵呵,看这里:

// 给3传一个函数,会打印3次:
(three(function () {
console.log('print 3 times');
}))();

// 给5传一个函数,会打印5次:
(five(function () {
console.log('print 5 times');
}))();

// 继续接着玩一会...

console 运行的结果还真是和预言的一样(让人头大)

1
2
3
4
5
6
7
8
print 3 times
print 3 times
print 3 times
print 5 times
print 5 times
print 5 times
print 5 times
print 5 times

怎么来解释这个现象呢?

我的解释

首先在我说我的解释之前看看一个叫 大肥肥的大飞爸爸 的网友的解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//定义数字2:# 廖老师解释的不清楚,其实按照数字1把2定义一次,就很容易推导了.
var two = function(f){
return function(x){
return f(f(x))
}
}
//打印两次也就是要执行2次f函数,也就是f(f(x)),可以看成f(g),g=f(x),初中数学的知识,我们要先求g=f(x),执行f(x)也就是打印了一次,现在得到了g=f(x),把g代入f(g),也就是又执行了一次f函数,又打印了一次,所以是2次.其中f(x)===one(f)(x)的return,还是拆开,f(g)也就是one(f)(g)===one(f)(f(x))===one(f)(one(f)(x)).这样应该都能看明白了~~
//同理,3的话就要打印3次也就是f(f(f(x))), 其中设g=f(f(x)),f(g)===one(f)(g),g是我们刚求的two(f)(x),代入得one(f)(two(f)(x)).再不懂自杀.

//那么乘法也就很好推导了:# one(f)(x)中,f是执行的次数.那么two(three(f))(x)就是把"打印3遍"执行2次.至于为什么是n(f)而不是n(f)(x),因为参数要求的是函数名.其他减法除法就不写了.
function multi(n, m) {
return function (f) {
return function (x) {
return m(n(f))(x);
}
}
}
//test six
var six = multi(three,two);
(multi(function () {
console.log('print 6 times');
}))();

这种解释方法是不错, 但是我觉得在 cs 里面遇到函数执行的分析如果不说 call stack 好像有点弄不清楚, 所以我就猜一下可能的道理.

首先看一下 zero 的定义

zero

再看一下 one 的定义

one

注意 one 中return 的值是函数 f, 也就是说他会执行一次函数 f.

在看 add 之前,我们想一想怎么能表示 two 这个函数呢?

开动脑筋, 无非是 return one(f)(one(f)(x))

three 呢?

return one(f)(one(f)(one(f)(x))

通俗的解释一下就是:

call stack 上压上 one 函数, 执行到return 会再压上 f 函数.
这个时候 f 函数 x 值带入什么呢? x = 又是一个 one 函数的嵌套.
那么再压上 one 函数, 执行到return 会再压上 f 函数……

仔细看看这不就是 add 的原理么?

add

至于 multiply, 和这个原理是相似的, 压上 m 函数的第一次, 带入 x, x = n 函数的嵌套; 压上 m 函数的第二次, 带入 x, x = n 函数的嵌套; ……

最后总共会执行 m*n次.

写在最后

如果您看到了这里, 应该会对我的理解有了一个了解. 如果您觉得我的理解是错误的, 欢迎在下面留言, 我们共同探讨.