[Lua] 尾调用消除(tail-call elimination)
《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)的更多相关文章
- Lua 正确的尾调用(proper tail call)
Lua支持“尾调用消除(tail-call elimination)”.尾调用(tail call):当一个函数调用是另一个函数的最后一个动作时,该调用才算是一条“尾调用”.例如,下面的代码就是一条“ ...
- PHP、Lua中的 尾调用
在程序设计中,递归(Recursion)是一个很常见的概念,合理使用递归,可以提升代码的可读性,但同时也可能会带来一些问题. 下面以阶乘(Factorial)为例来说明一下递归的用法,实现语言是PHP ...
- JavaScript中的尾调用优化
文章来源自:http://www.zhufengpeixun.com/qianduanjishuziliao/javaScriptzhuanti/2017-08-08/768.html JavaScr ...
- ES6躬行记(15)——箭头函数和尾调用优化
一.箭头函数 箭头函数(Arrow Function)是ES6提供的一个很实用的新功能,与普通函数相比,不但在语法上更为简洁,而且在使用时也有更多注意点,下面列出了其中的三点: (1)由于不能作为构造 ...
- ES6学习笔记 -- 尾调用优化
什么是尾调用? 尾调用(Tail Call)是函数式编程的一个重要概念,就是指某个函数的最后一步是调用另一个函数. function f(x) { return g(x) } 如上,函数 f 的最后一 ...
- iOS 的尾调用优化原理
背景: 今天聊代码规范的问题的时候说了一下尾调用的问题. 一:概念: 什么是尾调用? 尾调用(Tail Call):某个函数的最后一步仅仅只是调用了一个函数(可以是自身,可以是另一个函数). 注意 “ ...
- lua报错,看到报错信息有tail call,以为和尾调用有关,于是查了一下相关知识
尾调用是指在函数return时直接将被调函数的返回值作为调用函数的返回值返回,尾调用在很多语言中都可以被编译器优化, 基本都是直接复用旧的执行栈, 不用再创建新的栈帧, 原理上其实也很简单, 因为尾调 ...
- JavaScript 中的尾调用
尾调用(Tail Call) 尾调用是函数式编程里比较重要的一个概念,它的意思是在函数的执行过程中,如果最后一个动作是一个函数的调用,即这个调用的返回值被当前函数直接返回,则称为尾调用,如下所示: f ...
- lua如何调用C++函数
第一步是定义函数.所有在Lua中被调用的C/C++函数将使用下面一类指针进行调用: typedef int (*lua_CFunction) (lua_State *L); 换句话说,函数必须要以Lu ...
随机推荐
- SC review 5.2 设计可复用软件
行为子类型与Liskov替换原则 Java 中编译器执行的规则(静态类型检查): • 子类型可以增加方法,但不可删 • 子类型需要实现抽象类型中的所有未实现方法 • 子类型中重写的方法必须有相同或子类 ...
- python pandas dataframe 操作记录
从数据看select出数据后如何转换为dataframe df = DataFrame(cur.fetchall()) 如何更改列名,选取列,进行groupby操作 df.columns = ['me ...
- 25个增强iOS应用程序性能的提示和技巧 — 中级篇
本文由破船译自:raywenderlich 转载请注明出处:BeyondVincent的博客 _____________ 在开发iOS应用程序时.让程序具有良好的性能是非常关键的.这也是用户所期望的. ...
- Golang 临时对象池 sync.Pool
Go 1.3 的sync包中加入一个新特性:Pool.官方文档可以看这里http://golang.org/pkg/sync/#Pool 这个类设计的目的是用来保存和复用临时对象,以减少内存分配,降低 ...
- 使用transient关键字解决ehcache序列化错误
使用Ehcache时发现个不起眼的小问题 在一个Model中有以下代码: public class MyModel implements Serializable { private static f ...
- virtualbox+vagrant学习-2(command cli)-27-vagrant connect命令
Connect 命令: vagrant connect NAME connect命令通过启用对共享环境的访问来补充share命令.你可以在“vagrant share”部分了解有关vagrant sh ...
- Python之Pulsar框架使用
本文内容主要包含Pulsar的介绍和安装.初步使用.应用.常见示例等. 一. 介绍和安装 Pulsar是Python事件驱动并发框架:Pulsar具有高扩展性.高可用性的框架,它能够基于事件驱动的开源 ...
- 关于VS2010 C#使用DirectX的问题[英]
转载的,就不翻译了…微软把精力放到xna去了.所以推荐大家用XNA,如果非要用托管的DirectX也可以,只不过版本一直是2006年的了. 具体方法: 安装SDK之后 他默认的位置在C:\WINDOW ...
- 如何解析json字符串及返回json数据到前端
前言:最近需要实现的任务是:写若干个接口,并且接口中的请求数据是json格式,然后按照请求参数读取前端提前整理好的json数据,并且将json数据返回到服务器端. 主要的工具:Gson 2.8.2 ...
- 流程一直处于Running状态,应该怎么停止?
流程一直处于Running状态,应该怎么停止? 概述 我们有遇到这种情况:可能由于某些原因,流程发起后一直处于Running状态,然后我们想Stop掉这些出问题的流程,这个时候你在Workspace里 ...