在JS中能解释下,这两个生产函数名词解释表达式为什么可以这样写吗

在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
问题对人有帮助,内容完整,我也想知道答案
问题没有实际价值,缺少关键内容,没有改进余地
(function () {
...//这里写代码
...//直接在外面写代码
这样写有什么好处吗?二者有分别吗,感觉都一样的,不确定求高手分析,谢谢!
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
ES5中没有块级作用域,所以我们用立即执行函数来制造一个块级作用域。这样可以防止每个模块的代码不小心污染到全局变量。
ps,这叫立即执行匿名函数
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
(function(){
var name = '草泥马';
(function(){
var name = '草泥马二号';
简单的说就是这两只草泥马处于两个不想干的平行宇宙
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
通过函数创建新的作用域,在内部存储私有变量,防止命名冲突或内部变量被改写。
答案对人有帮助,有参考价值
答案没帮助,是错误的答案,答非所问
尽可能少的暴露变量到全局作用域
同步到新浪微博
分享到微博?
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
我要该,理由是:
在 SegmentFault,学习技能、解决问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。  Javascript Function无处不在,而且功能强大!通过Javascript函数可以让JS具有面向对象的一些特征,实现封装、继承等,也可以让代码得到复用。但事物都有两面性,Javascript函数有的时候也比较&任性&,你如果不了解它的&性情&,它很可能给你制造出一些意想不到的麻烦(bugs)出来。 &
  Javascript Function有两种类型:
  1)函数声明(Function Declaration);
// 函数声明
function funDeclaration(type){
return type==="Declaration";
  2)函数表达式(Function Expression)。
// 函数表达式
var funExpression = function(type){
return type==="Expression";
  上面的代码看起来很类似,感觉也没什么太大差别。但实际上,Javascript函数上的一个&陷阱&就体现在Javascript两种类型的函数定义上。下面看两段代码(分别标记为代码1段和代码2段):
funDeclaration("Declaration");//=& true
function funDeclaration(type){
return type==="Declaration";
funExpression("Expression");//=&error
var funExpression = function(type){
return type==="Expression";
  用函数声明创建的函数funDeclaration可以在funDeclaration定义之前就进行调用;而用函数表达式创建的funExpression函数不能在funExpression被赋值之前进行调用。为什么会这样呢?!这就要理解Javascript Function两种类型的区别:用函数声明创建的函数可以在函数解析后调用(解析时进行等逻辑处理);而用函数表达式创建的函数是在运行时进行赋值,且要等到表达式赋值完成后才能调用。这个区别看似微小,但在某些情况下确实是一个难以发现的陷阱。出现这个陷阱的本质原因体现在这两种类型在Javascript function hoisting(函数提升)和运行时机(解析时/运行时)上的差异。关于,可参见我的另外一篇博文。上面两段代码的函数提升可示意为下图: 代码1段JS函数等同于:
function funDeclaration(type){
return type==="Declaration";
funDeclaration("Declaration");//=& true
代码2段JS函数等同于:
funExpression("Expression");//==&error
funExpression = function(type){
return type==="Expression";
  上述代码在运行时,只定义了funExpression变量,但值为undefined。因此不能在undefined上进行函数调用。此时funExpression赋值语句还没执行到。为了进一步加深JS函数两种类型的区别,下面给出一个更具迷惑性的示例,请看下面的代码(代码段4):
console.log(typeof (sayHey));//=&function
console.log(typeof (sayHo));//=&undefined
if (true) {
function sayHey() {
console.log("sayHey");
sayHello = function sayHo() {
console.log("sayHello");
function sayHey() {
console.log("sayHey2");
sayHello = function sayHo() {
console.log("sayHello2");
sayHey();// =& sayHey2
sayHello();// =& sayHello
  分析:sayHey是用函数声明创建的,在JS解析时JS编译器将函数定义进行了函数提升,也就是说,在解析JS代码的时候,JS编译器(条件判断不形成新的作用域,两个sayHey函数定义都被提升到条件判断之外)检测到作用域内有两个同名的sayHey定义,第一个定义先被提升,第二个定义接着被提升(第二个定义在第一个定义之下),第二个定义覆盖了第一个sayHey定义,所以sayHey()输出sayHey2;而sayHello是用函数表达式创建的,其表达式的内容是在JS运行时(不是解析时)才能确定(这里条件判断就起到作用了),所以sayHello表达式执行了第一个函数定义并赋值,则sayHello()输出sayHello。
  代码段4的代码实际上等同于下面的代码(代码段5):
function sayHey() {
console.log("sayHey");
function sayHey() {
console.log("sayHey2");
console.log(typeof (sayHey));//=&function
console.log(typeof (sayHo));//=&undefined
if (true) {
//hoisting...
sayHello = function sayHo() {
console.log("sayHello");
//hoisting...
sayHello = function sayHo() {
console.log("sayHello2");
sayHey();// =& sayHey2
sayHello();// =& sayHello
  有的人也许会怀疑函数sayHey的定义是第二个覆盖第一个了么?我们可以把sayHey的源代码进行输出,有图有真相,如下图所示:
  Javascript 中函数声明和函数表达式是存在区别的,函数声明在JS解析时进行函数提升,因此在同一个作用域内,不管函数声明在哪里定义,该函数都可以进行调用。而函数表达式的值是在JS运行时确定,并且在表达式赋值完成后,该函数才能调用。这个微小的区别,可能会导致JS代码出现意想不到的bug,让你陷入莫名的陷阱中。
  最后附上代码段4中sayHello和sayHey两个函数的核心步骤(个人理解,若有异议欢迎留言探讨):
  上图为sayHello函数执行的主要步骤示意图。
  上图为sayHey函数执行主要步骤的示意图。若对感兴趣,可以看另外一篇博文
阅读(...) 评论()JavaScript 匿名函数/lambda表达式的一次使用a year ago要递归,即需要在函数体内部调用自己,所以给函数取个名字(将函数绑定到改名字)然后通过该名字引用自己,这种方式很直观,上面的实现也就是这么做的。上面使用了赋值语句实现函数名与函数的绑定,名字的绑定还可以通过参数的传递来实现,例如:( (msg) =& console.log(msg) ) ("hello world")
该匿名函数调用时,在函数体作用域内,调用时参数值被绑定到了参数 msg。对于上面的调用,函数体中,值 "hello world" 被绑定到了变量 msg。
回到递归的话题,同这个例子一样,我们可以将函数作为参数传给自己,于是在函数体内得到了对自己的引用,然后进行递归操作,比较自然的可以写成下面这样(为了清晰,我们对这个简单的函数也进行了折行处理):
(fact, n) =&
: n * fact(n-1)
注意这是一个函数表达式,接收两个参数,第一个 fact 用于传入自己;第二个 n,我们要求的是 n 的阶乘。上面这个表达式有个显著的错误,思考一下,你能指出吗?
由于 fact 用于传入自己,即该函数表达式自身,所以,fact 应该是上面描述的那个函数,应该有两个参数,所以函数体中的调用 fact(n-1) 是显然错误的,其第一个参数应该是该函数本身,巧的是,fact 引用的就是该函数本身,于是修正后如下:
(fact, n) =&
: n * fact(fact, n-1)
现在,我们怎么把该函数传递给自己呢?读者可以自行考虑一下再往下读。答案惊人的简单,我们将它拷贝一份传给自己就好了,于是得到:
((fact, n) =&
: n * fact(fact, n-1))(
(fact, n) =&
: n * fact(fact, n-1),
注意,n 为自由变量,我们再将其套进函数体将 n 作为参数即可:
((fact, n) =&
: n * fact(fact, n-1))(
(fact, n) =&
: n * fact(fact, n-1),
现在,我们成功的剔除了赋值只通过匿名函数实现了函数的递归,你可以在解释器中验证,例如下面传入求 10 的阶乘:
((n) =& ((fact, n) =& n === 0 ? 1 : n * fact(fact, n-1))((fact, n) =& n === 0 ? 1 : n * fact(fact, n-1), n))(10) // =& fact(10) =& 326800
上面这种直接拷贝的方式简单直接,但是作为一名合格的程序员,看见这种重复的东西显然要更近一步将其剔除。想法也很简单,将这个重复的东西绑定到一个变量,然后通过该变量消除重复即可。
再次,我们不使用赋值语句进行变量绑定,还是通过参数绑定实现,很简单(读者可以自行尝试再看下面实现),得到
((fact) =& fact(fact, n))(
(fact, n) =&
: n * fact(fact, n-1)
对于阶乘这个实例,我们可以到此为止了。你可以将其它递归函数改成该种模式,比如这个被用烂了的非常低效的求 fibnacii 数的例子:((n) =&
((fib) =& fib(fib, n))(
(fib, n) =& {
if (n &= 0) {
if (n === 1) {
return fib(fib, n-1) + fib(fib, n-2);
如果你做进一步抽象,可以得到这样一个函数(太晚了,过程就不写了,可能以后补上?):
(h) =& ((f) =& f(f))((f) =& h(() =& f(f)))
有什么作用呢,有了它,上面哪两个函数可以这样写:
((h) =& ((f) =& f(f))((f) =& h(() =& f(f))))(
(y) =& (n) =& n === 0 ? 1 : n * y()(n-1)
((h) =& ((f) =& f(f))((f) =& h(() =& f(f))))(
(y) =& (n) =& {
if (n &= 0) {
if (n === 1) {
return y()(n-1) + y()(n-2);
可以看见,其中 y 是个很神奇的东西,你可以自己琢磨琢磨(是可以还从前面两个例子出发进行抽象)或者,阅读下面第二个参考资料后再动手尝试。文中思路来自于 的 the little ... 系列中的某一本(忘了哪本了)赞赏1 人赞赏22收藏分享举报文章被以下专栏收录{&debug&:false,&apiRoot&:&&,&paySDK&:&https:\u002F\u002Fpay.zhihu.com\u002Fapi\u002Fjs&,&wechatConfigAPI&:&\u002Fapi\u002Fwechat\u002Fjssdkconfig&,&name&:&production&,&instance&:&column&,&tokens&:{&X-XSRF-TOKEN&:null,&X-UDID&:null,&Authorization&:&oauth c3cef7c66aa9e6a1e3160e20&}}{&database&:{&Post&:{&&:{&isPending&:false,&contributes&:[{&sourceColumn&:{&lastUpdated&:,&description&:&&,&permission&:&COLUMN_PUBLIC&,&memberId&:1489039,&contributePermission&:&COLUMN_PUBLIC&,&translatedCommentPermission&:&all&,&canManage&:true,&intro&:&&,&urlToken&:&lotuc&,&id&:25191,&imagePath&:&v2-4d6d6da948a1a0d7503720edbab5ea97.jpg&,&slug&:&lotuc&,&applyReason&:&0&,&name&:&lotuc&,&title&:&lotuc&,&url&:&https:\u002F\u002Fzhuanlan.zhihu.com\u002Flotuc&,&commentPermission&:&COLUMN_ALL_CAN_COMMENT&,&canPost&:true,&created&:,&state&:&COLUMN_NORMAL&,&followers&:635,&avatar&:{&id&:&v2-4d6d6da948a1a0d7503720edbab5ea97&,&template&:&https:\u002F\u002Fpic2.zhimg.com\u002F{id}_{size}.jpg&},&activateAuthorRequested&:false,&following&:false,&imageUrl&:&https:\u002F\u002Fpic2.zhimg.com\u002Fv2-4d6d6da948a1a0d7503720edbab5ea97_l.jpg&,&articlesCount&:16},&state&:&accepted&,&targetPost&:{&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-ac4d83b71ac_r.jpg&,&lastUpdated&:,&imagePath&:&v2-ac4d83b71ac.jpg&,&permission&:&ARTICLE_PUBLIC&,&topics&:[],&summary&:&匿名函数\u002Flambda表达式很有意思。这里我们在 JavaScript 中通过只使用匿名函数实现递归。\n 考虑一个简单的求阶乘的函数: let fact = (n) =& n === 0 ? 1 : n * fact(n-1)\nfact(10) \u002F\u002F =& 3628800 要递归,即需要在函数体内部调用自己,所以给函数取个名字…&,&copyPermission&:&ARTICLE_COPYABLE&,&translatedCommentPermission&:&all&,&likes&:0,&origAuthorId&:0,&publishedTime&:&T00:56:16+08:00&,&sourceUrl&:&&,&urlToken&:,&id&:2129220,&withContent&:false,&slug&:,&bigTitleImage&:false,&title&:&JavaScript 匿名函数\u002Flambda表达式的一次使用&,&url&:&\u002Fp\u002F&,&commentPermission&:&ARTICLE_ALL_CAN_COMMENT&,&snapshotUrl&:&&,&created&:,&comments&:0,&columnId&:25191,&content&:&&,&parentId&:0,&state&:&ARTICLE_PUBLISHED&,&imageUrl&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-ac4d83b71ac_r.jpg&,&author&:{&bio&:&&,&isFollowing&:false,&hash&:&9bebf466fc2535bec442451dca7cd13a&,&uid&:56,&isOrg&:false,&slug&:&lotuc&,&isFollowed&:false,&description&:&http:\u002F\u002Flotuc.org&,&name&:&lotuc&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Flotuc&,&avatar&:{&id&:&9e3316ee52eff43c624b1d&,&template&:&https:\u002F\u002Fpic4.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&memberId&:1489039,&excerptTitle&:&&,&voteType&:&ARTICLE_VOTE_CLEAR&},&id&:522565}],&title&:&JavaScript 匿名函数\u002Flambda表达式的一次使用&,&author&:&lotuc&,&content&:&匿名函数\u002Flambda表达式很有意思。这里我们在 JavaScript 中通过只使用匿名函数实现递归。\n\u003Cbr\u003E\u003Cbr\u003E考虑一个简单的求阶乘的函数:\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&kd\&\u003Elet\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E10\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&c1\&\u003E\u002F\u002F =& 3C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E要递归,即需要在函数体内部调用自己,所以给函数取个名字(将函数绑定到改名字)然后通过该名字引用自己,这种方式很直观,上面的实现也就是这么做的。上面使用了赋值语句实现函数名与函数的绑定,名字的绑定还可以通过参数的传递来实现,例如:\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Emsg\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Econsole\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E.\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Elog\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Emsg\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&s2\&\u003E\&hello world\&\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E该匿名函数调用时,在函数体作用域内,调用时参数值被绑定到了参数 msg。对于上面的调用,函数体中,值 \&hello world\& 被绑定到了变量 msg。\n\u003Cbr\u003E\u003Cbr\u003E回到递归的话题,同这个例子一样,我们可以将函数作为参数传给自己,于是在函数体内得到了对自己的引用,然后进行递归操作,比较自然的可以写成下面这样(为了清晰,我们对这个简单的函数也进行了折行处理):\n\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E注意这是一个函数表达式,接收两个参数,第一个 fact 用于传入自己;第二个 n,我们要求的是 n 的阶乘。上面这个表达式有个显著的错误,思考一下,你能指出吗?\n\u003Cbr\u003E\u003Cbr\u003E由于 fact 用于传入自己,即该函数表达式自身,所以,fact 应该是上面描述的那个函数,应该有两个参数,所以函数体中的调用 fact(n-1) 是显然错误的,其第一个参数应该是该函数本身,巧的是,fact 引用的就是该函数本身,于是修正后如下:\n\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E现在,我们怎么把该函数传递给自己呢?读者可以自行考虑一下再往下读。\u003Cbr\u003E\u003Cbr\u003E答案惊人的简单,我们将它拷贝一份传给自己就好了,于是得到:\n\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))(\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E),\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E注意,n 为自由变量,我们再将其套进函数体将 n 作为参数即可:\n\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))(\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E),\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E))\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E现在,我们成功的剔除了赋值只通过匿名函数实现了函数的递归,你可以在解释器中验证,例如下面传入求 10 的阶乘:\n\u003Cbr\u003E\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E),\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))(\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E10\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&c1\&\u003E\u002F\u002F =& fact(10) =& 3C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E上面这种直接拷贝的方式简单直接,但是作为一名合格的程序员,看见这种重复的东西显然要更近一步将其剔除。想法也很简单,将这个重复的东西绑定到一个变量,然后通过该变量消除重复即可。\n\u003Cbr\u003E\u003Cbr\u003E再次,我们不使用赋值语句进行变量绑定,还是通过参数绑定实现,很简单(读者可以自行尝试再看下面实现),得到\n\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))(\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\n
\u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efact\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E))\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E对于阶乘这个实例,我们可以到此为止了。你可以将其它递归函数改成该种模式,比如这个被用烂了的非常低效的求 fibnacii 数的例子:\u003Cbr\u003E\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))(\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E&=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E+\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Efib\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E,\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E2\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E))\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E\u003Cbr\u003E如果你做进一步抽象,可以得到这样一个函数(太晚了,过程就不写了,可能以后补上?):\n\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eh\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Eh\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(()\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)))\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cbr\u003E有什么作用呢,有了它,上面哪两个函数可以这样写:\n\u003Cbr\u003E\u003Cbr\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-js\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E\u003Cspan class=\&c1\&\u003E\u002F\u002F fact\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eh\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Eh\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(()\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))))(\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E?\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E:\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E*\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E()(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\n\u003Cspan class=\&c1\&\u003E\u002F\u002F fib\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Eh\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))((\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Eh\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(()\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ef\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E))))(\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003Ey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E=&\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E&=\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E0\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Eif\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E===\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&p\&\u003E{\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E;\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n
\u003Cspan class=\&k\&\u003Ereturn\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E()(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E1\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E \u003Cspan class=\&o\&\u003E+\u003C\u002Fspan\u003E \u003Cspan class=\&nx\&\u003Ey\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E()(\u003C\u002Fspan\u003E\u003Cspan class=\&nx\&\u003En\u003C\u002Fspan\u003E\u003Cspan class=\&o\&\u003E-\u003C\u002Fspan\u003E\u003Cspan class=\&mi\&\u003E2\u003C\u002Fspan\u003E\u003Cspan class=\&p\&\u003E);\u003C\u002Fspan\u003E\n
\u003Cspan class=\&p\&\u003E}\u003C\u002Fspan\u003E\n\u003Cspan class=\&p\&\u003E)\u003C\u002Fspan\u003E\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E可以看见,其中 y 是个很神奇的东西,你可以自己琢磨琢磨(是可以还从前面两个例子出发进行抽象)或者,阅读下面第二个参考资料后再动手尝试。\u003C\u002Fp\u003E\u003Cbr\u003E\u003Cbr\u003E\u003Cul\u003E\u003Cli\u003E文中思路来自于\u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fmitpress.mit.edu\u002Fauthors\u002Fdaniel-p-friedman\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EDaniel P. Friedman\u003C\u002Fa\u003E 的 the little ... 系列中的某一本(忘了哪本了)\u003C\u002Fli\u003E\u003Cli\u003E\u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FFixed-point_combinator\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EFixed-point combinator\u003C\u002Fa\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E&,&updated&:new Date(&T16:56:16.000Z&),&canComment&:false,&commentPermission&:&anyone&,&commentCount&:2,&collapsedCount&:0,&likeCount&:22,&state&:&published&,&isLiked&:false,&slug&:&&,&lastestTipjarors&:[{&isFollowed&:false,&name&:&C0deTrash&,&headline&:&&,&avatarUrl&:&https:\u002F\u002Fpic3.zhimg.com\u002Fv2-cdb8231afbe80_s.jpg&,&isFollowing&:false,&type&:&people&,&slug&:&1ychee&,&bio&:&攻城狮&,&hash&:&2c3fd0d9e1d4ef6895b3fdb5c7fc4de1&,&uid&:330400,&isOrg&:false,&description&:&&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002F1ychee&,&avatar&:{&id&:&v2-cdb8231afbe80&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false}],&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic4.zhimg.com\u002Fv2-ac4d83b71ac_r.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&reviewers&:[],&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Lambda 演算&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&JavaScript&}],&adminClosedComment&:false,&titleImageSize&:{&width&:1920,&height&:1080},&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&column&:{&slug&:&lotuc&,&name&:&lotuc&},&tipjarState&:&activated&,&tipjarTagLine&:&真诚赞赏,手留余香&,&sourceUrl&:&&,&pageCommentsCount&:2,&tipjarorCount&:1,&annotationAction&:[],&hasPublishingDraft&:false,&snapshotUrl&:&&,&publishedTime&:&T00:56:16+08:00&,&url&:&\u002Fp\u002F&,&lastestLikers&:[{&bio&:null,&isFollowing&:false,&hash&:&757a9569b4adc20b635d7&,&uid&:08,&isOrg&:false,&slug&:&wook9615&,&isFollowed&:false,&description&:&&,&name&:&忞博&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fwook9615&,&avatar&:{&id&:&v2-cae692fe8cadeb301f90b&,&template&:&https:\u002F\u002Fpic2.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&铲屎小能手&,&isFollowing&:false,&hash&:&6cbf7d7efbf29ee739c0d5&,&uid&:530200,&isOrg&:false,&slug&:&tian-guo-tou-er&,&isFollowed&:false,&description&:&&,&name&:&甜过头儿&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Ftian-guo-tou-er&,&avatar&:{&id&:&da8e974dc&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&不学无术&,&isFollowing&:false,&hash&:&6311174dbfc047bcbe2a5ad&,&uid&:48,&isOrg&:false,&slug&:&lsdsjy&,&isFollowed&:false,&description&:&&,&name&:&lsdsjy&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Flsdsjy&,&avatar&:{&id&:&c279aea6f01bdf6aec89b&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:null,&isFollowing&:false,&hash&:&a65bba33e359bfb0e5335d2&,&uid&:52,&isOrg&:false,&slug&:&eta-100a&,&isFollowed&:false,&description&:&&,&name&:&eta-100a&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Feta-100a&,&avatar&:{&id&:&da8e974dc&,&template&:&https:\u002F\u002Fpic3.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},{&bio&:&并不是挤地铁上班的IT码农,我搭公交&,&isFollowing&:false,&hash&:&2bedb334f466e395e380be6&,&uid&:80,&isOrg&:false,&slug&:&cai-jun-jie-82&,&isFollowed&:false,&description&:&&,&name&:&蔡俊杰&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Fcai-jun-jie-82&,&avatar&:{&id&:&v2-d1daabface259e00c045f&,&template&:&https:\u002F\u002Fpic1.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false}],&summary&:&匿名函数\u002Flambda表达式很有意思。这里我们在 JavaScript 中通过只使用匿名函数实现递归。\n 考虑一个简单的求阶乘的函数: let fact = (n) =& n === 0 ? 1 : n * fact(n-1)\nfact(10) \u002F\u002F =& 3628800 要递归,即需要在函数体内部调用自己,所以给函数取个名字…&,&reviewingCommentsCount&:0,&meta&:{&previous&:{&isTitleImageFullScreen&:false,&rating&:&none&,&titleImage&:&https:\u002F\u002Fpic2.zhimg.com\u002F50\u002Fv2-bb7aa62f4e7909bdfb8ec8ede8f3ed33_xl.jpg&,&links&:{&comments&:&\u002Fapi\u002Fposts\u002F2Fcomments&},&topics&:[{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&Racket&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&解释器&},{&url&:&https:\u002F\u002Fwww.zhihu.com\u002Ftopic\u002F&,&id&:&&,&name&:&程序设计语言设计&}],&adminClosedComment&:false,&href&:&\u002Fapi\u002Fposts\u002F&,&excerptTitle&:&&,&author&:{&bio&:&&,&isFollowing&:false,&hash&:&9bebf466fc2535bec442451dca7cd13a&,&uid&:56,&isOrg&:false,&slug&:&lotuc&,&isFollowed&:false,&description&:&http:\u002F\u002Flotuc.org&,&name&:&lotuc&,&profileUrl&:&https:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002Flotuc&,&avatar&:{&id&:&9e3316ee52eff43c624b1d&,&template&:&https:\u002F\u002Fpic4.zhimg.com\u002F{id}_{size}.jpg&},&isOrgWhiteList&:false,&isBanned&:false},&content&:&\u003Cp\u003E审校:\u003Ca class=\&member_mention\& href=\&http:\u002F\u002Fwww.zhihu.com\u002Fpeople\u002F802e5a4b8b40cc8dad620302\& data-hash=\&802e5a4b8b40cc8dad620302\& data-hovercard=\&p$b$802e5a4b8b40cc8dad620302\&\u003E@MrMathematica\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E原文:\u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=http%3A\u002F\u002Fcs.brown.edu\u002Fcourses\u002Fcs173\u002FFbook\u002Findex.html\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EPLAI 第二版\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003EGitHub:\u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fgithub.com\u002Flotuc\u002FPLAI-cn\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EPLAI-cn\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003EGitBook:\u003Ca href=\&http:\u002F\u002Flink.zhihu.com\u002F?target=https%3A\u002F\u002Fwww.gitbook.com\u002Fbook\u002Flotuc\u002Fplai-cn\& class=\& wrap external\& target=\&_blank\& rel=\&nofollow noreferrer\&\u003EPLAI-cn\u003C\u002Fa\u003E\u003C\u002Fp\u003E\u003Cp\u003E翻译声明见 Github 仓库\u003C\u002Fp\u003E\u003Chr\u003E\u003Ch2\u003E5 添加函数\u003C\u002Fh2\u003E\u003Cp\u003E下面尝试将其变成真正的语言。比如说可以添加诸如条件语句这个特性,但是一个语言要真正变得有意思,它需要函数或者某种等价于函数的东西。所以我们就直接来添加函数好了。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E练习\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E给语言添加条件语句。你可以选择添加布尔类型,或者方便起见,你的条件语句可以将0视作false,其他值视作 true。\u003C\u002Fblockquote\u003E\u003Cp\u003E想象一下,我们要构造一个类似于DrRacket的系统。程序员在定义(definitions)窗口中定义函数,然后在交互(interactions)窗口中使用它们。我们先假设函数只能在定义窗口定义;交互窗口中只能出现表达式(这些限制会随着内容的深入被解除)。按此假定,当运行程序时,函数已经被解析可供使用。所以,我们给解释器添加一个参数——函数定义的集合。\u003C\u002Fp\u003E\u003Cblockquote\u003E注意这里我们说的是函数的\u003Cb\u003E集合\u003C\u002Fb\u003E,也就是说,任何函数的定义中可以引用任意其它函数。这是我有意的设计。当你设计自己的语言时,记住注意考虑这一点。\u003C\u002Fblockquote\u003E\u003Ch2\u003E5.1 定义函数的数据表示\u003C\u002Fh2\u003E\u003Cp\u003E简单起见,我们仅考虑只有一个参数的函数。下面是一些Racket函数的例子:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E(define (double x) (+ x x))\n\n(define (quadruple x) (double (double x)))\n\n(define (const5 _) 5)\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E\u003Cb\u003E练习\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E如果函数可以带有多个参数呢?参数名之间有什么限制?\u003C\u002Fblockquote\u003E\u003Cp\u003E函数的定义包含哪些内容?它包含名字(上文中的\u003Ccode\u003Edouble\u003C\u002Fcode\u003E,\u003Ccode\u003Equadruple\u003C\u002Fcode\u003E,\u003Ccode\u003Econst5\u003C\u002Fcode\u003E),我们将使用符号(symbol)类型表示(\u003Ccode\u003E'double\u003C\u002Fcode\u003E等);\u003Cb\u003E形参\u003C\u002Fb\u003E(formal parameter,形式参数的简写)(例如\u003Ccode\u003Ex\u003C\u002Fcode\u003E),也使用符号类型表示(\u003Ccode\u003E'x\u003C\u002Fcode\u003E);最后还有函数体。我们后面会一步一步完善函数体的表示法,现阶段函数定义的数据类型如下:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&fundef& ::=函数定义\n\n
(define-type FunDefC\n
[fdC (name : symbol) (arg : symbol) (body : ExprC)])\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E所以函数体是什么呢?显然它可以是算术表达式,且有时候应该可以使用\u003Ccode\u003EArithC\u003C\u002Fcode\u003E语言来表示:例如,函数\u003Ccode\u003Econst5\u003C\u002Fcode\u003E的函数体可以使用\u003Ccode\u003E(numC 5)\u003C\u002Fcode\u003E表示。但是要表示\u003Ccode\u003Edouble\u003C\u002Fcode\u003E函数的函数体需要更多东西:不仅需要加法(我们已经定义了),还需要“x”。你可能会称它\u003Cb\u003E变量\u003C\u002Fb\u003E(variable),但是现在我们不使用该术语,我们叫它\u003Cb\u003E标识符\u003C\u002Fb\u003E(identifier)。\u003C\u002Fp\u003E\u003Cblockquote\u003E后文我还会进一步解释这两个命名。\u003C\u002Fblockquote\u003E\u003Cp\u003E\u003Cb\u003E思考题\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E还有别的吗?\u003C\u002Fblockquote\u003E\u003Cp\u003E最后,我们看看\u003Ccode\u003Equadruple\u003C\u002Fcode\u003E的函数体,它包含另一种结构:函数\u003Cb\u003E调用\u003C\u002Fb\u003E(application)。要特别注意函数\u003Cb\u003E定义\u003C\u002Fb\u003E和\u003Cb\u003E调用\u003C\u002Fb\u003E的区别。函数定义描述了函数是什么,而调用则是对函数的使用。里面一层的\u003Ccode\u003Edouble\u003C\u002Fcode\u003E 函数调用使用的\u003Cb\u003E实参\u003C\u002Fb\u003E(actual parameter,实际参数的简写)是\u003Ccode\u003Ex\u003C\u002Fcode\u003E;外面的那层的\u003Ccode\u003Edouble\u003C\u002Fcode\u003E调用使用的参数是\u003Ccode\u003E(double x)\u003C\u002Fcode\u003E。可以看到,参数可以是任意表达式。\u003C\u002Fp\u003E\u003Cp\u003E下面我们尝试把上面所有的东西糅合到一个数据类型中。显然我们需要扩展已有的语法(因为我们还想保留算术运算)。我们给新的数据类型一个新名字以示区别:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&exprC& ::=表达式\n\n
(define-type ExprC\n
[numC (n : number)]\n
&idC-def&标识符的定义\n
&app-def&调用的定义\n
[plusC (l : ExprC) (r : ExprC)]\n
[multC (l : ExprC) (r : ExprC)])\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E标识符与形参关系紧密。当调用函数时,我们传给它某个值,从效果来说是将函数体中出现的形参实例——所有同名标识符——替换为该值。【注释】为了简化这个搜索——替换过程,不妨使用与形参相同的数据类型来表示标识符。形参的数据类型已经定好了,于是:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&idC-def& ::=标识符的定义\n\n
[idC (s : symbol)]\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cblockquote\u003E这里我们忽略了几个问题:“值”是什么?何时替换?后文会继续讨论。\u003C\u002Fblockquote\u003E\u003Cp\u003E最后,函数调用。它包含两个部分:函数名和(实际)参数。上面已经说过参数可以为任意表达式(包括标识符和函数调用)。至于函数名,让其和函数定义中的函数名类型一致符合直觉,就这样做吧:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&app-def& ::=调用的定义\n\n
[appC (fun : symbol) (arg : ExprC)]\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E该定义简单明了,函数名指明要调用哪个函数,然后后面提供函数调用所需参数。\u003C\u002Fp\u003E\u003Cp\u003E有了定义,看看之前的三个函数该怎么表示:\u003C\u002Fp\u003E\u003Cul\u003E\u003Cli\u003E\u003Ccode\u003E(fdC 'double 'x (plusC (idC 'x) (idC 'x)))\u003C\u002Fcode\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ccode\u003E(fdC 'quadruple 'x (appC 'double (appC 'double (idC 'x))))\u003C\u002Fcode\u003E\u003C\u002Fli\u003E\u003Cli\u003E\u003Ccode\u003E(fdC 'const5 'x (numC 5))\u003C\u002Fcode\u003E\u003C\u002Fli\u003E\u003C\u002Ful\u003E\u003Cp\u003E下面还需要选择函数定义集合的表示法。使用列表类型就蛮方便。\u003C\u002Fp\u003E\u003Cblockquote\u003E小心!你有没有注意到,之前我们说这是函数定义的\u003Cb\u003E集合\u003C\u002Fb\u003E,然而实现却选用了\u003Cb\u003E列表\u003C\u002Fb\u003E。也就是说,我们用有序的数据结构去表达无序的数据。那么,测试时,至少我们应该试用各种不同顺序的函数定义,以确保我们没有不小心引入了(影响结果的)顺序。\u003C\u002Fblockquote\u003E\u003Ch2\u003E5.2 开始实现解释器\u003C\u002Fh2\u003E\u003Cp\u003E于是我们可以开始实现解释器了。首先考虑解释器的输入是什么。之前,只需要传入一个表达式即可,现在它还需要传入函数定义的列表。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&interp& ::=解释器\n\n
(define (interp [e : ExprC] [fds : (listof FunDefC)]) : number\n
&interp-body&)解释器主体\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E稍微回顾一下我们前面实现的解释器(第三章)。遇到数,显然还是直接返回该数作为结果;遇到加法和乘法,还是应该一样递归的求值。递归时该用什么作函数定义呢?由于求值过程中,既不需要添加也不需要移除函数\u003Cb\u003E定义\u003C\u002Fb\u003E,即函数定义集合保持不变,在递归时函数定义应该原封不动的往下传递。\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&interp-body& ::=解释器主体\n\n
(type-case ExprC e\n
[numC (n) n]\n
&idC-interp-case&解释之标识符子句\n
&appC-interp-case&解释之调用子句\n
[plusC (l r) (+ (interp l fds) (interp r fds))]\n
[multC (l r) (* (interp l fds) (interp r fds))])\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E接下来实现函数调用。首先我们需要从函数定义中寻找对应的函数定义,我们可以假设如下的帮助函数可以实现此功能:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E; get-fundef : symbol * (listof FunDefC) -& FunDefC\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E假设我们已经找到了函数的定义,下一步要对其函数体求值。还记得之前说过函数调用该怎么求值?搜索标识符并将其替换为实际参数。这个搜索替换过程足够重要,值得花一小节讨论,5.4节我们再回过来实现解释器。\u003C\u002Fp\u003E\u003Ch2\u003E5.3 替换\u003C\u002Fh2\u003E\u003Cp\u003E替换是将一个表达式(这里是函数体)中某个名字(这里是形参)替换成另一个表达式(这里是实参)的过程。首先确定其类型:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E; subst : ExprC * symbol * ExprC -& ExprC\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E将参数名起的有意义些:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&subst& ::=替换\n\n
(define (subst [what : ExprC] [for : symbol] [in : ExprC]) : ExprC\n
&subst-body&)替换函数的主体\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E在\u003Ccode\u003Ein\u003C\u002Fcode\u003E表达式中,将\u003Ccode\u003Efor\u003C\u002Fcode\u003E替换成\u003Ccode\u003Ewhat\u003C\u002Fcode\u003E。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考题\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E考虑之前几个示例函数的函数体,将参数\u003Ccode\u003Ex\u003C\u002Fcode\u003E替换为\u003Ccode\u003E3\u003C\u002Fcode\u003E的结果是什么?\u003C\u002Fblockquote\u003E\u003Cp\u003E对于\u003Ccode\u003Edouble\u003C\u002Fcode\u003E函数来说,结果为\u003Ccode\u003E(+ 3 3)\u003C\u002Fcode\u003E;对于\u003Ccode\u003Equadruple\u003C\u002Fcode\u003E,结果为\u003Ccode\u003E(double (double 3))\u003C\u002Fcode\u003E;对于\u003Ccode\u003Econst5\u003C\u002Fcode\u003E,结果就为\u003Ccode\u003E5\u003C\u002Fcode\u003E(函数体中没有出现\u003Ccode\u003Ex\u003C\u002Fcode\u003E所以也没有替换)。\u003C\u002Fp\u003E\u003Cblockquote\u003E对于\u003Ccode\u003Edouble\u003C\u002Fcode\u003E一个常见的错误是将其替换成\u003Ccode\u003E(define (double x) (+ 3 3))\u003C\u002Fcode\u003E。替换发生在\u003Cb\u003E函数调用时\u003C\u002Fb\u003E,此时只需要函数体就可以了。函数定义头部的作用是找到函数,还有给出参数的名称;但是计算其值时只需要函数体。如果用整个函数定义进行替换,试试看你会得到哪种类型错误。\u003C\u002Fblockquote\u003E\u003Cp\u003E这个例子几乎涵盖了所有情况。如果是数的话,无需替换任何东西;如果是标识符,例子没有覆盖标识符\u003Cb\u003E不同\u003C\u002Fb\u003E的情况,你也能想到该怎么做:保留之;其它情况,递归的替换各子表达式。\u003C\u002Fp\u003E\u003Cp\u003E在开始写代码之前,还有一种重要情况要考虑一下。假设我们要替换的标识符恰巧是某个函数名称,该怎么处理呢?\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考题\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E该怎么处理呢?\u003C\u002Fblockquote\u003E\u003Cp\u003E对于这个问题,有多种处理方法。一种方案是从设计上来来考虑:函数名有其自己的“世界”,它和程序中其它标识符都不同。某些语言(例如C和Common Lisp,尽管它们的做法也略有不同)采取这种策略,根据标识符使用的位置将其解析到不同的\u003Cb\u003E命名空间\u003C\u002Fb\u003E。而其他一些语言则不做这样的区分。我们很快会研究这么一种语言(后文)。\u003C\u002Fp\u003E\u003Cp\u003E现在,我们从务实的角度来处理这个问题。由于这里表达式求值结果是数,这就要问函数名能求值成数不。但是,数不能命名函数,只有符号能。所以进行这种替换是没有意义的,函数名和要替换的符号没有关系。(比如,某个函数的参数可以叫\u003Ccode\u003Ex\u003C\u002Fcode\u003E,其函数体中又可以调用另一个名为\u003Ccode\u003Ex\u003C\u002Fcode\u003E的函数,两者被区别处理。)\u003C\u002Fp\u003E\u003Cp\u003E决定做完了,是时候写代码了:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&subst-body& ::=替换函数的主体\n\n
(type-case ExprC in\n
[numC (n) in]\n
[idC (s) (cond\n
[(symbol=? s for) what]\n
[else in])]\n
[appC (f a) (appC f (subst what for a))]\n
[plusC (l r) (plusC (subst what for l)\n
(subst what for r))]\n
[multC (l r) (multC (subst what for l)\n
(subst what for r))])\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E\u003Cb\u003E练习\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E请注意,在numC子句,解释器返回\u003Ccode\u003En\u003C\u002Fcode\u003E,而替换函数返回\u003Ccode\u003Ein\u003C\u002Fcode\u003E(即原始表达式,在这个位置等价于\u003Ccode\u003E(numC n)\u003C\u002Fcode\u003E)。为什么?\u003C\u002Fblockquote\u003E\u003Ch2\u003E5.4 继续实现解释器\u003C\u002Fh2\u003E\u003Cp\u003E搞定了替换的实现(我们这么认为),我们来完成解释器。替换这步干了很多事,好在函数调用的很多细节都在其中完成了。很自然的想法是:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&appC-interp-case-take-1& ::=解释之调用子句,第一次尝试\n\n
[appC (f a) (local ([define fd (get-fundef f fds)])\n
(subst a\n
(fdC-arg fd)\n
(fdC-body fd)))]\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E但是这是错的。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考题\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E看出错在哪里了吗?\u003C\u002Fblockquote\u003E\u003Cp\u003E从类型角度考察,解释器的函数返回类型是什么?数。替换函数的返回类型呢?表达式。比如说,在替换\u003Ccode\u003Edouble\u003C\u002Fcode\u003E的函数体是,可能得到的结果是\u003Ccode\u003E(+ 5 5)\u003C\u002Fcode\u003E的表达形式。这并不是解释器的合法返回值。需要进一步对其求值。所以应该这么做:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&appC-interp-case& ::=解释之调用子句\n\n
[appC (f a) (local ([define fd (get-fundef f fds)])\n
(interp (subst a\n
(fdC-arg fd)\n
(fdC-body fd))\n
fds))]\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E好了,还剩下最后一个子句:标识符。这个能有多复杂呢?看上去标识符类似数那样简单!然而我们把它留到最后处理,这说明到了它的处理可能有点微妙或者说有点复杂。\u003C\u002Fp\u003E\u003Cp\u003E\u003Cb\u003E思考题\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E请尝试一些例子,从而理解标识符该怎么处理。\u003C\u002Fblockquote\u003E\u003Cp\u003E假设\u003Ccode\u003Edouble\u003C\u002Fcode\u003E函数定义如下:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E(define (double x) (+ x y))\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E我们把\u003Ccode\u003Ex\u003C\u002Fcode\u003E替换成\u003Ccode\u003E5\u003C\u002Fcode\u003E,得到\u003Ccode\u003E(+ 5 y)\u003C\u002Fcode\u003E。没毛病,然而剩下的\u003Ccode\u003Ey\u003C\u002Fcode\u003E该怎么替换呢?事实上从一开始我们就应该意识到这个\u003Ccode\u003Edouble\u003C\u002Fcode\u003E定义是\u003Cb\u003E错误的\u003C\u002Fb\u003E。标识符\u003Ccode\u003Ey\u003C\u002Fcode\u003E被称为\u003Cb\u003E自由的(free)\u003C\u002Fb\u003E,这是个负面的词。\u003C\u002Fp\u003E\u003Cp\u003E换一种说法,解释器应该永远也遇不到标识符。在解释过程中,所有标识符应该都会被替换掉(被称为\u003Cb\u003E被绑定\u003C\u002Fb\u003E的,这是种正面的说法)。因此,当解释器直面标识符时,只能这么处理:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E&idC-interp-case& ::=解释之标识符子句\n\n
[idC (_) (error 'interp \&shouldn't get here\&)]不应执行到这里\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E这样我们的解释器就完成了!\u003C\u002Fp\u003E\u003Cp\u003E最后,为了完整,我们还需要实现\u003Ccode\u003Eget-fundef\u003C\u002Fcode\u003E:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E;获取函数定义\n(define (get-fundef [n : symbol] [fds : (listof FunDefC)]) : FunDefC\n
[(empty? fds) (error 'get-fundef \&reference to undefined function\&)]引用未定义的函数\n
[(cons? fds) (cond\n
[(equal? n (fdC-name (first fds))) (first fds)]\n
[else (get-fundef n (rest fds))])]))\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Ch2\u003E5.5 等等,还没完呢!\u003C\u002Fh2\u003E\u003Cp\u003E之前\u003Ccode\u003Esubst\u003C\u002Fcode\u003E的类型我们说是:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E; subst : ExprC * symbol * ExprC -& ExprC\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E简单起见我们这里用表面语法描述问题,假设我们在解释\u003Ccode\u003E(double (+ 1 2))\u003C\u002Fcode\u003E。它会将所有\u003Ccode\u003Ex\u003C\u002Fcode\u003E都替换为\u003Ccode\u003E(+ 1 2)\u003C\u002Fcode\u003E,于是解释器得到表达式\u003Ccode\u003E(+ (+ 1 2) (+ 1 2))\u003C\u002Fcode\u003E。这是我们想要的吗?\u003C\u002Fp\u003E\u003Cp\u003E在学习代数时,可能你的老师不是这么教你的:首先应该将参数规约成其结果(在这个例子中就是\u003Ccode\u003E3\u003C\u002Fcode\u003E),然后将参数替换为这个结果。这么说,替换的类型就应该是:\u003C\u002Fp\u003E\u003Cdiv class=\&highlight\&\u003E\u003Cpre\u003E\u003Ccode class=\&language-text\&\u003E\u003Cspan\u003E\u003C\u002Fspan\u003E; subst : number * symbol * ExprC -& ExprC\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E\u003C\u002Fdiv\u003E\u003Cp\u003E请注意,我们不能直接把数放入表达式,而是必须先把数放入到numC调用中。所以,一种可行的做法是,\u003Ccode\u003Esubst\u003C\u002Fcode\u003E函数可以把第一个参数用numC包装起来然后调用辅助函数。(事实上,现有的\u003Ccode\u003Esubst\u003C\u002Fcode\u003E函数就可以是这个辅助函数:它接收的第一个参数的类型是ExprC,那么当传给它的数据是numC类型时显然没问题。)\u003C\u002Fp\u003E\u003Cblockquote\u003E事实上,替换的实现还是不太对!这里的替换函数仅仅能处理我们的示例语言,过了就不行了。这给问题也很微妙,它被称为“名称捕获”。解决这个问题是复杂,巧妙和令人兴奋的智力工作。不过这里我不打算往这个方向发展。所以本书中我们绕过此问题。不过如果你对此感兴趣,请阅读\u003Cb\u003Elambda演算\u003C\u002Fb\u003E方面的书籍,它们会提供帮助正确地实现替换。\u003C\u002Fblockquote\u003E\u003Cp\u003E\u003Cb\u003E练习\u003C\u002Fb\u003E\u003C\u002Fp\u003E\u003Cblockquote\u003E修改}

我要回帖

更多关于 消费函数名词解释 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信