《Lua程序设计(第2版)》 6.3 正确的尾调用(proper tail call)

  Lua是支持尾调用消除(tail-call elimination)的,如下面对函数g的调用就是尾调用。

 function f(x) return g(x) end

  尾调用之后,程序不需要保存任何关于函数f的栈(stack)信息,即不耗费任何栈空间。

  尾调用方法可用于编写状态机(state machine),类似于goto到另一个函数,如果没有尾调用消除,每次调用都会创建一个新的栈层(stack level),若干步之后有可能导致栈溢出。

  注意,以下几种情况均不是尾调用:

 function f(x)
g(x)
end return g(x) +
return x or g(x)
return (g(x))

  举例,书中有个迷宫的例子,代码如下:

入口

room1

room2
room3

出口

room4

  

 function room1 ()
local direction = io.read()
if direction == "east" then
room2()
elseif direction == "south" then
room3()
else
print("invalid direction!")
room1()
end
end function room2 ()
local direction = io.read()
if direction == "west" then
return room1()
elseif direction == "south" then
return room4()
else
print("invalid direction!")
return room2()
end
end function room3 ()
local direction = io.read()
if direction == "north" then
return room1()
elseif direction == "east" then
return room4()
else
print("invalid direction!")
return room3()
end end function room4 ()
print("congratulations!")
end room1()

运行结果:

south
west
invalid direction!
east
congratulations!

我们写一个尾调用的简单循环:

 function room1()
return room2()
end function room2()
print()
return room1()
end room1()

运行结果:

 -- 一直循环不会报错

不使用尾调用:

 function room1()
room2()
end function room2()
print()
room1()
end room1()

运行结果:

 -- 循环几秒之后出现以下报错:

lua: stack overflow
stack traceback:
[C]: in function 'print'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
...
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in function 'room2'
6_propertailcall.lua:: in function 'room1'
6_propertailcall.lua:: in main chunk
[C]: ?

[Lua] 尾调用消除(tail-call elimination)的更多相关文章

  1. Lua 正确的尾调用(proper tail call)

    Lua支持“尾调用消除(tail-call elimination)”.尾调用(tail call):当一个函数调用是另一个函数的最后一个动作时,该调用才算是一条“尾调用”.例如,下面的代码就是一条“ ...

  2. PHP、Lua中的 尾调用

    在程序设计中,递归(Recursion)是一个很常见的概念,合理使用递归,可以提升代码的可读性,但同时也可能会带来一些问题. 下面以阶乘(Factorial)为例来说明一下递归的用法,实现语言是PHP ...

  3. JavaScript中的尾调用优化

    文章来源自:http://www.zhufengpeixun.com/qianduanjishuziliao/javaScriptzhuanti/2017-08-08/768.html JavaScr ...

  4. ES6躬行记(15)——箭头函数和尾调用优化

    一.箭头函数 箭头函数(Arrow Function)是ES6提供的一个很实用的新功能,与普通函数相比,不但在语法上更为简洁,而且在使用时也有更多注意点,下面列出了其中的三点: (1)由于不能作为构造 ...

  5. ES6学习笔记 -- 尾调用优化

    什么是尾调用? 尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是调用另一个函数. function f(x) { return g(x) } 如上,函数 f 的最后一 ...

  6. iOS 的尾调用优化原理

    背景: 今天聊代码规范的问题的时候说了一下尾调用的问题. 一:概念: 什么是尾调用? 尾调用(Tail Call):某个函数的最后一步仅仅只是调用了一个函数(可以是自身,可以是另一个函数). 注意 “ ...

  7. lua报错,看到报错信息有tail call,以为和尾调用有关,于是查了一下相关知识

    尾调用是指在函数return时直接将被调函数的返回值作为调用函数的返回值返回,尾调用在很多语言中都可以被编译器优化, 基本都是直接复用旧的执行栈, 不用再创建新的栈帧, 原理上其实也很简单, 因为尾调 ...

  8. JavaScript 中的尾调用

    尾调用(Tail Call) 尾调用是函数式编程里比较重要的一个概念,它的意思是在函数的执行过程中,如果最后一个动作是一个函数的调用,即这个调用的返回值被当前函数直接返回,则称为尾调用,如下所示: f ...

  9. lua如何调用C++函数

    第一步是定义函数.所有在Lua中被调用的C/C++函数将使用下面一类指针进行调用: typedef int (*lua_CFunction) (lua_State *L); 换句话说,函数必须要以Lu ...

随机推荐

  1. 洛谷 P2045 方格取数加强版【费用流】

        题目链接:https://www.luogu.org/problemnew/show/P2045 题目描述 给出一个n*n的矩阵,每一格有一个非负整数Aij,(Aij <= 1000)现 ...

  2. HAproxy.md

    HAProxy HAProxy是什么 HAProxy(High Available Proxy)是基于四层和七层的高可用负载均衡代理服务器,配置简单.支持多达上万条并发请求. HAProxy工作原理 ...

  3. 将项目发布到Maven中央仓库的不完整纪要

    背景 有几个Utils性质的Jar需要跨项目引用,原本想部署私有Maven仓库,后来感觉太麻烦,索性直接发布到中央库,引用时也方便. 发布成功之后,觉得某些细节还是有必要记录一下. 资源 Sonaty ...

  4. BZOJ4571:[SCOI2016]美味(主席树,贪心)

    Description 一家餐厅有 n 道菜,编号 1...n ,大家对第 i 道菜的评价值为 ai(1≤i≤n).有 m 位顾客,第 i 位顾客的期望值为 bi,而他的偏好值为 xi . 因此,第 ...

  5. 'vue-cli-service' 不是内部或外部命令,也不是可运行的程序 或批处理文件。

    首先把 node_modules 文件夹删除 然后运行以下命令: cnpm install 这样就可以正常运行

  6. 20145203 实验五 Java网络编程及安全

    20145203 实验五 Java网络编程及安全 实验内容 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.设计安全传输系统. 实验要求 1.基于Java Socket实现安全传输 2 ...

  7. P1441 砝码称重

    题目描述 现有n个砝码,重量分别为a1,a2,a3,……,an,在去掉m个砝码后,问最多能称量出多少不同的重量(不包括0). 输入输出格式 输入格式: 输入文件weight.in的第1行为有两个整数n ...

  8. window.open 防止浏览器拦截

    https://blog.csdn.net/sinat_37255207/article/details/89374416 网上试了很多方法 最终只有一种可以 var newWin = window. ...

  9. NAS、SAN、ISCSI存储

    前提补充:linux下只要能够mount存储盘到目录,则这个存储盘必定是格式化过了的 NAS1.客户端通过TCP/IP协议连接存储服务器的存储卷.2.客户端---网络交换机---存储服务器3.存储服务 ...

  10. 通过R语言统计考研英语(二)单词出现频率

    通过R语言统计考研英语(二)单词出现频率 大家对英语考试并不陌生,首先是背单词,就是所谓的高频词汇.厚厚的一本单词,真的看的头大.最近结合自己刚学的R语言,为年底的考研做准备,想统计一下最近考研英语( ...