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

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

  1. function f(x) return g(x) end

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

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

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

  1. function f(x)
  2. g(x)
  3. end
  4.  
  5. return g(x) +
  6. return x or g(x)
  7. return (g(x))

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

入口

room1

room2
room3

出口

room4

  

  1. function room1 ()
  2. local direction = io.read()
  3. if direction == "east" then
  4. room2()
  5. elseif direction == "south" then
  6. room3()
  7. else
  8. print("invalid direction!")
  9. room1()
  10. end
  11. end
  12.  
  13. function room2 ()
  14. local direction = io.read()
  15. if direction == "west" then
  16. return room1()
  17. elseif direction == "south" then
  18. return room4()
  19. else
  20. print("invalid direction!")
  21. return room2()
  22. end
  23. end
  24.  
  25. function room3 ()
  26. local direction = io.read()
  27. if direction == "north" then
  28. return room1()
  29. elseif direction == "east" then
  30. return room4()
  31. else
  32. print("invalid direction!")
  33. return room3()
  34. end
  35.  
  36. end
  37.  
  38. function room4 ()
  39. print("congratulations!")
  40. end
  41.  
  42. room1()

运行结果:

  1. south
  2. west
  3. invalid direction!
  4. east
  5. congratulations!

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

  1. function room1()
  2. return room2()
  3. end
  4.  
  5. function room2()
  6. print()
  7. return room1()
  8. end
  9.  
  10. room1()

运行结果:

  1. -- 一直循环不会报错

不使用尾调用:

  1. function room1()
  2. room2()
  3. end
  4.  
  5. function room2()
  6. print()
  7. room1()
  8. end
  9.  
  10. room1()

运行结果:

  1. -- 循环几秒之后出现以下报错:
  2.  
  3. lua: stack overflow
  4. stack traceback:
  5. [C]: in function 'print'
  6. 6_propertailcall.lua:: in function 'room2'
  7. 6_propertailcall.lua:: in function 'room1'
  8. 6_propertailcall.lua:: in function 'room2'
  9. 6_propertailcall.lua:: in function 'room1'
  10. 6_propertailcall.lua:: in function 'room2'
  11. 6_propertailcall.lua:: in function 'room1'
  12. 6_propertailcall.lua:: in function 'room2'
  13. 6_propertailcall.lua:: in function 'room1'
  14. 6_propertailcall.lua:: in function 'room2'
  15. ...
  16. 6_propertailcall.lua:: in function 'room2'
  17. 6_propertailcall.lua:: in function 'room1'
  18. 6_propertailcall.lua:: in function 'room2'
  19. 6_propertailcall.lua:: in function 'room1'
  20. 6_propertailcall.lua:: in function 'room2'
  21. 6_propertailcall.lua:: in function 'room1'
  22. 6_propertailcall.lua:: in function 'room2'
  23. 6_propertailcall.lua:: in function 'room1'
  24. 6_propertailcall.lua:: in main chunk
  25. [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. SC review 5.2 设计可复用软件

    行为子类型与Liskov替换原则 Java 中编译器执行的规则(静态类型检查): • 子类型可以增加方法,但不可删 • 子类型需要实现抽象类型中的所有未实现方法 • 子类型中重写的方法必须有相同或子类 ...

  2. python pandas dataframe 操作记录

    从数据看select出数据后如何转换为dataframe df = DataFrame(cur.fetchall()) 如何更改列名,选取列,进行groupby操作 df.columns = ['me ...

  3. 25个增强iOS应用程序性能的提示和技巧 — 中级篇

    本文由破船译自:raywenderlich 转载请注明出处:BeyondVincent的博客 _____________ 在开发iOS应用程序时.让程序具有良好的性能是非常关键的.这也是用户所期望的. ...

  4. Golang 临时对象池 sync.Pool

    Go 1.3 的sync包中加入一个新特性:Pool.官方文档可以看这里http://golang.org/pkg/sync/#Pool 这个类设计的目的是用来保存和复用临时对象,以减少内存分配,降低 ...

  5. 使用transient关键字解决ehcache序列化错误

    使用Ehcache时发现个不起眼的小问题 在一个Model中有以下代码: public class MyModel implements Serializable { private static f ...

  6. virtualbox+vagrant学习-2(command cli)-27-vagrant connect命令

    Connect 命令: vagrant connect NAME connect命令通过启用对共享环境的访问来补充share命令.你可以在“vagrant share”部分了解有关vagrant sh ...

  7. Python之Pulsar框架使用

    本文内容主要包含Pulsar的介绍和安装.初步使用.应用.常见示例等. 一. 介绍和安装 Pulsar是Python事件驱动并发框架:Pulsar具有高扩展性.高可用性的框架,它能够基于事件驱动的开源 ...

  8. 关于VS2010 C#使用DirectX的问题[英]

    转载的,就不翻译了…微软把精力放到xna去了.所以推荐大家用XNA,如果非要用托管的DirectX也可以,只不过版本一直是2006年的了. 具体方法: 安装SDK之后 他默认的位置在C:\WINDOW ...

  9. 如何解析json字符串及返回json数据到前端

    前言:最近需要实现的任务是:写若干个接口,并且接口中的请求数据是json格式,然后按照请求参数读取前端提前整理好的json数据,并且将json数据返回到服务器端. 主要的工具:Gson  2.8.2 ...

  10. 流程一直处于Running状态,应该怎么停止?

    流程一直处于Running状态,应该怎么停止? 概述 我们有遇到这种情况:可能由于某些原因,流程发起后一直处于Running状态,然后我们想Stop掉这些出问题的流程,这个时候你在Workspace里 ...