Lua中的尾调用消除(tail-call eliminat)

本文分享Lua中的尾调用消除(tail-call eliminat)

什么是尾调用

在日常Lua的使用中, 我们经常会在一个方法的执行过程中调用其它方法.

在这些方法中, 有一种特殊的形式值得大家注意, 可以做到一定程度上的优化.

这个形式也就是所谓的尾调用, 顾名思义就是一个方法最后的跳出点是一个方法(包括递归调用自己)的调用.

形如return func(args)的形式, 比如下面的例子:

function test1() 
end

function test2(xxx) 
end

function test3()
	print("---------------->")
    
    if switch then
    	return test1()
    end
    
    return test2(1)
end

上面的11行和14行都是尾调用.

哪些是真正的尾调用

尾调用的重点是如何判断一个调用是不是尾调用.

要注意的就是一定是return func(args)的形式.

其他的形式都不是, 比如下面这些:

function test1()
end

-- example 1
function test()
	test1()
end

-- example 2
function test()
	return 1 and test1()
end

-- example 3
function test()
	return test1() or 2
end

为什么只有return func(args)的形式才是尾调用呢?

因为尾调用的真正判断是该方法的调出点调用另一个方法, 另一个方法的返回值需不要本方法来处理(空返回值也是返回值).

  • example 1, 会返回一个nil, 且在test中被抛弃.
  • example 2和3, 会返回一个nil, 且在test与剩余的表达式做处理
  • 如果另一个方法的返回值在本方法中不需要做任何处理, 则是尾调用, 即通过return关键字直接返回.

尾调用消除

上面和大家分享了什么是尾调用, 现在介绍尾调用消除.

在介绍尾调用消除之前, 我们需要知道一个理论, 也就是方法的调用过程.

通用语言的方法调用过程

本质上说我们所有的代码都是执行在某个方法中的, lua的代码是在方法或者代码块中.

当我们调用一个方法时, 语言会为这个方法分配一个函数栈(一般都这么叫).

在该函数中的所有局部变量, 全局变量, 方法调用等都是通过这个函数栈来执行的. 即函数的执行是以栈的形式来执行.

在方法中调用另一个方法的时候, 方法2会记录方法1的返回点, 能让方法2执行完毕后能够回到方法1中.

这种设计是为了方法1能够处理方法2的返回值, 所以需要保存方法1的函数栈.

在调用另一个方法的时候, 也会进行函数栈的分配, 在方法2返回之前, 内存中就存在两个函数栈(哪怕是同一个方法被递归调用).

随着调用的方法越来越多, 内存中的函数栈就越来越多, 甚至会内存不足, 造成内存溢出.

具体大家可以去了解下计算机的基础知识, 这里不再展开来讲.

优化

所谓尾调用消除就是为了解决这种情况而存在的.

它的原理很简单, 就是通过特定的形式调用告诉程序, 对于另一个方法的调用我不再关心它的返回值, 请你帮我把本方法的函数栈销毁.

这样内存中就不会出现无用的函数栈, 最后甚至造成内存溢出.

只要使用尾调用的写法, 我们甚至可以一直递归调用到天荒地老而不用担心内存爆炸(_).

总结

尾调用只是一种特定情况下的形式, 如果我们在本方法中对另一个方法的返回值有兴趣, 那么就不能用尾调用了.

在我们日常游戏开发中, 有大量的回调函数执行, 特别是事件分发可能会引起一系列的后续方法调用, 使用尾调用就很有必要了.

在递归方法的设计中, 使用尾调用也能有效避免内存浪费和内存溢出的问题.

希望对大家有所帮助.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拂面清风三点水

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值