【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
之前的只是开胃小菜,在确保你能弄懂前面的代码例子后再来看这个。这个能弄懂表示你已经彻底搞懂了闭包是怎么回事了。
前面的例子中,我们最终还是需要一个数来表示一个起始的数。那么还没有完全达到我们的目的。但是如果要完全用函数来代替数,我们又怎么证明一个函数确实是表示那个数呢?
为此,我们需要先考察一下数到底是怎么回事。
想想最原始的表示数的方法:用指头数数,当10个指头数完了,数就到头了,如果你是一个牧羊人,有超过十头的羊,还可以用来表示比10更大的数。但是当人类想明白5个指头和5头羊之间的联系是什么的时候,他们就不用再用5头羊或5个指头来表示5,而只要说出数字5的符号就可以了,这时人类完成了一次抽象概念提取的过程。
因此,最原始的数的概念:一个数就是表示一件事物重复的次数。
想象一个在完全只有函数,而没有数的世界里,怎么计数?方法就是,对这个函数传入一个函数执行(注意,没有数了),这个函数的内容被执行N遍,就表示执行这个函数的函数代表的是N这个数了。
因此,假设有一个函数能代表0
zero = f(x)
还有一个函数能代表加1
incr = f(x)
那么1就能表示为
one = incr(zero)
2表示为
two = incr(incr(zero))
更多的数表示为
three = incr(incr(incr(zero)))
four = incr(incr(incr(incr(zero))))
five = incr(incr(incr(incr(incr(zero)))))
…………
自然数N能表示为incr的N次方(zero)
所以,我们首先要定义一个函数用来表示zero,和incr,这样就能表示其他所有的数了
接下来,这是zero的表示
function zero(f){
return function(x){
return x;
}
}
这个函数表示的意思是,传入一个函数f,zero会返回给你一个函数,用zero返回的函数,再传入一个x,这时zero返回的函数会把这个x原样返回。这个函数看起来没什么用,但是它符合前面我们对表示数的函数的定义,传入f,它一次都不会被执行。那么x在这里是干什么的呢?
x本身无任何意义,它只是用来占位的。表示一个数存在的边界,这有点像C语言字符串数组中的'\0',任何字符串数组中都不会显示它,但任何字符串数组都离不开它。
注意:接下来你会看到很多形如n(f)(x)的式子,为了大家在看的时候不犯晕,这里着重强调一下,希望大家牢记在心:如果n是一个表示函数的数的话,那么n(f)(x)的意思就是把f(x)调用n次。
你也许会认为用一个repeat(n,f,x)的函数可以做同样的事情,但我需要提醒你的是:首先,n不是一个数字,n是一个关于几是几的抽象。此外这种设计会得到一个比repeat更抽象,灵活,强大的函数,希望你能在后面领悟得到。
在这个例子中我们用两个打印鸭子的函数来表示f,x,f打印黄鸭子,表示的是增1,x打印绿色鸭子,表示的是0。
function yellowduck(x){
print('<img src="http://thumbs.dreamstime.com/thumbimg_426/1249659187Yb2Igm.jpg">');
}
function greenduck(x){
print('<img src="http://www.greenduck.co.uk/images/about/logo.gif">');
}
注意到,两个方法中的入参在我们这里的示例中其实没有任何作用,因为不管传入的x是什么,都只打印一只鸭子而已。
尴尬的是,传入的打印绿鸭子的方法从来不会被调用,就好比我们在每一个字符串数组中都添加了\0,但\0从来不会被显示一样。因此,还需要专门写一个方法来查看打印数的情况,人为的在打印完原数后再打印一只绿鸭子表示结尾,printduck只是为了方便我们查看一个数而已。
function printduck(n){
n(yellowduck)(greenduck);//greenduck never be called at this line;
greenduck(null);//execute it manually;
}
如果看到一个函数被执行,打印出一只绿鸭子,表示为0,打印出一只黄鸭子后面跟一只绿鸭子表示此数为1。
这是打印0的代码。
<!DOCTYPE html>
<html>
<body>
<script language="javascript" type="text/javascript">
<!--
function print(){
var vars = print.arguments;
var arr = [];
for (var i = 0; i < vars.length; i++){
arr.push(vars[i]);
}
document.write(arr.join(',')+'<br/>');
}
function yellowduck(x){
print('<img src="http://thumbs.dreamstime.com/thumbimg_426/1249659187Yb2Igm.jpg">');
}
function greenduck(x){
print('<img src="http://www.greenduck.co.uk/images/about/logo.gif">');
}
function zero(f){
return function(x){
return x;
}
}
function printduck(n){
n(yellowduck)(greenduck);//greenduck never be called at this line;
greenduck(null);//execute it manually;
}
接下来再看增1是如何定义的
function incr(n){
return function(f){
return function(x){
return f(n(f)(x));
}
}
}
看着
是不是有点晕?别急,让我们拆开来看里面的函数,把一行拆成两行就好理解了。
function incr(n){
return function(f){
return function(x){
executed = n(f)(x);
return f(executed);
}
}
}
前面着重说了,形如n(f)(x)的式子,表示将f(x)执行n遍。因此该函数就是在执行n遍后,再一次调用f(x),不就是增加一遍了吗?
试想当我们执行incr(zero)(yellowduck)(greenduck)的时候。我们得到的是
yellowduck(zero(yellowduck)(greenduck));
前面已经分析过,zero(f)(x)不会执行任何东西。因此,式子的结果就是yellowduck被执行一次。
所以
one = incr(zero);
再来看
incr(one)(yellowduck)(greenduck)的情形,等于
yellowduck(one(yellowduck)(greenduck));
而one(yellowduck)(greenduck)又等于yellowduck(zero(yellowduck)(greenduck));带入上式,得到
yellowduck(yellowduck(zero(yellowduck)(greenduck)));
将这个过程一直持续下去,我们将得到
n = yellowduck(yellowduck........(yellowduck(zero(yellowduck)(greenduck)));
等于调用了n次yellowduck(x),和我们之前的定义是完全一致的。
我们可以执行来看看实际情况如何
var one = incr(zero);
var two = incr(one);
var three = incr(two);
printduck(one);
printduck(two);
printduck(three);
神奇的事情发生了!在我们的例子中确实没有一个数的存在,但我们确实用函数组成的函数,把数的概念给表达了出来!
(待续)
来源:oschina
链接:https://my.oschina.net/u/1167335/blog/145010