如何用函数表示数(四)数的彻底消失

时光怂恿深爱的人放手 提交于 2019-12-17 21:35:15

【推荐】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);
神奇的事情发生了!在我们的例子中确实没有一个数的存在,但我们确实用函数组成的函数,把数的概念给表达了出来!

(待续)

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!