本站文章均为Jensen抹茶喵原创,转载务必在明显处注明:
转载自【博客园】 原文链接:http://www.cnblogs.com/JensenCat/p/5112420.html

1.什么是闭包

支持闭包特性通常需要一个嵌套函数,通过执行嵌套函数来改变所在父函数的局部变量状态,父函数保存调用上下文状态,而嵌套函数负责修改状态的改变.(简单来说就是得支持函数嵌套)

下面就是一个Lua闭包:

2.闭包的作用

下面是一个java的类,getName方法获取到了类对象的私有成员变量

class Person
{private String name;public String getName(){return name;    }
}

 通过上面的方式可以获取到一个类内部的私有属性,同样的,在lua中可以通过某个方法来获取这个方法的局部变量,然后通过这个方法内的方法来读取想要的变量值。

 function func3()local num3 = 44function func4()return num3endreturn func4
endlocal func = func3();
print(func())

解释:

1.在外部无法获取到func3内部的局部变量,但是func3内部的局部方法func4却可以获取到,因此返回一个func4的引用 ,这样在外部通过这个func4就可以获取到func3的内部变量。

2.虽然是绕了一个圈子,但是在方法外部却通过这样一个手段获取到了内部的值。而这个方法内的局部方法func4就叫做闭包,按照很多书上的概念,这个方法搭建了方法内部与方法外部的桥梁,使得在外部也可以任意的获取到方法内部的资源。

3.但是闭包会造成变量在内存中持久占用,因此会有一定的性能问题,最好不要轻易使用,即便使用也要在恰当的实际进行释放。

3.游戏开发中的应用

--以下用cocos2dx中的Lua来举例...
--2dx通过tolua++把类方法导出--举例api
--按钮响应回调函数格式为:
--luaFunc(event)
--event为触摸按下,触摸移动,触摸离开等事件--lua中的API为:
--UIButton::addListenHandler(luaFunc)--实际需求是我按钮按下时,我需要改变按钮自身的纹理...此时回调中却没有按钮本身的对象(sender),怎么办呢?--利用闭包就轻松解决了--下面是LUA实战例子:一个testUI的页面类
local testUI = testUI or {}local testUI:onBtnClick(sender,event)--可获取的参数有:隐藏的self,btn,event
endfunction testUI:initButton()local btn = UIButton:create()--重点来了btn:addListenHandler(function(event)--使用闭包把self,btn都传进去了....self:onBtnClick(btn,event)end)
endreturn testUI

4.lua函数递归以及尾调用消除

1)递归示例:

--反面递归例子(递归必须在初始化以后才能调用)
local func = function(n)if n > 0 then return func(n - 1) --此处调用错误
end--正确例子1
local func
func = function(n)if n > 0 then return func(n - 1) --此处调用错误
end--正确例子2(此处函数展开后解释为例子1的代码再执行)
function func(n)if n > 0 then return func(n - 1) --此处调用错误
end--如果是两个函数嵌套递归(超前递归,必须先声明)
local g
local f--这里不能加local..不然等于声明了多一个局部变量了,递归的对象就不对了
function g()f()
end--这里不能加local..不然等于声明了多一个局部变量了,递归的对象就不对了
function f()g()
end

2)尾调用消除(递归的时候如果返回的函数是最后的执行...则不损耗栈空间,相当于GOTO语句)

--尾调用消除
function g()return a,b
end--正确例子
function f()return g() --正确的尾调用消除
end--错误例子1
function f(x)return g() + 1 --最后执行的是加法
end--错误例子2
function f(x)return (g()) --最后执行的是强制返回1个值
end--错误例子3
function f(x)return x or g()
end
 总结:由于LUA尾递归调用这个性质,我们可以用GOTO来实现状态机了

=====================================================================

Lua函数可以被当成参数传递,也可以被当成结果返回,在函数体中仍然可以定义内嵌函数。lua闭包是Lua函数生成的数据对象。每个闭包可以有一个upvalue值,或者多个闭包共享一个upvalue数值。

1、upvalue

如果函数f2定义在函数f1中,那么f2为f1的内嵌函数,f1为f2的外包函数,外包和内嵌都具有传递性,即f2的内嵌必然是f1的内嵌,而f1的外包也一定是f2的外包。

内嵌函数可以访问外包函数已经创建的局部变量,而这些局部变量则称为该内嵌函数的外部局部变量(或者upvalue)

代码如下:

function f1(n)-- 函数参数也是局部变量local function f2()print(n) -- 引用外包函数的局部变量endreturn f2
end
g1 = f1(1979)
g1() -- 打印出1979
g2 = f1(500)
g2() -- 打印出500

当执行完g1 = f1(1979)后,局部变量n的生命本该结束,但因为它已经成了内嵌函数f2的upvalue,它又被赋给了变量g1,所以它仍然能以某种形式继续“存活”下来,从而令g1()打印出正确的值。

2、闭包

  Lua编译一个函数时,其中包含了函数体对应的虚拟机指令、函数用到的常量值(数,文本字符串等等)和一些调试信息。在运行时,每当Lua执行一个形如function...end 这样的函数时,它就会创建一个新的数据对象,其中包含了相应函数原型的引用、环境(用来查找全局变量的表)的引用以及一个由所有upvalue引用组成的数组,而这个数据对象就称为闭包。由此可见,函数是编译期概念,是静态的,而闭包是运行期概念,是动态的。g1和g2的值严格来说不是函数而是闭包,并且是两个不相同的闭包,而这两个闭包保有各自的upvalue值。
  
使用upvalue代码如下:

function f1(n)local function f2()print(n)endn = n + 10return f2
endg1 = f1(1979)
g1() -- 打印出1989

g1()打印出来的是1989,原因是打印的是upvalue的值。

upvalue实际是局部变量,而局部变量是保存在函数堆栈框架上的,所以只要upvalue还没有离开自己的作用域,它就一直生存在函数堆栈上。这种情况下,闭包将通过指向堆栈上的upvalue的引用来访问它们,一旦upvalue即将离开自己的作用域,在从堆栈上消除之前,闭包就会为它分配空间并保存当前的值,以后便可通过指向新分配空间的引用来访问该upvalue。当执行到f1(1979)的n = n + 10时,闭包已经创建了,但是变量n并没有离开作用域,所以闭包仍然引用堆栈上的n,当return f2完成时,n即将结束生命,此时闭包便将变量n(已经是1989了)复制到自己管理的空间中以便将来访问。

3、upvalue和闭包数据共享

upvalue还可以为闭包之间提供一种数据共享的机制。

(1)单重内嵌函数的闭包 (函数创建的闭包)

一个函数创建的闭包共享一份upvalue。

代码如下:

function Create(n)local function foo1()print(n)endlocal function foo2()n = n + 10endreturn foo1,foo2
endf1,f2 = Create(1979)--创建闭包
f1() -- 打印1979
f2()
f1() -- 打印1989
f2()
f1() -- 打印1999

  f1,f2这两个闭包的原型分别是Create中的内嵌函数foo1和foo2,而foo1和foo2引用的upvalue是同一个,即Create的局部变量n。执行完Create调用后,闭包会把堆栈上n的值复制出来,那么是否f1和f2就分别拥有一个n的拷贝呢?其实不然,当Lua发现两个闭包的upvalue指向的是当前堆栈上的相同变量时,会聪明地只生成一个拷贝,然后让这两个闭包共享该拷贝,这样任一个闭包对该upvalue进行修改都会被另一个探知。上述例子很清楚地说明了这点:每次调用f2都将upvalue的值增加了10,随后f1将更新后的值打印出来。upvalue的这种语义很有价值,它使得闭包之间可以不依赖全局变量进行通讯,从而使代码的可靠性大大提高。

(2)多重内嵌函数的闭包 (闭包创建的闭包)

同一闭包创建的其他的闭包共享一份upvalue。

闭包在创建之时其需要的变量就已经不在堆栈上,而是引用更外层外包函数的局部变量(实际上是upvalue)。

function Test(n)local function foo()local function inner1()print(n)endlocal function inner2()n = n + 10endreturn inner1,inner2endreturn foo
endt = Test(1979)--创建闭包(共享一份upvalue)
f1,f2 = t()--创建闭包
f1()        -- 打印1979
f2()
f1()        -- 打印1989
g1,g2 = t()
g1()        -- 打印1989
g2()
g1()        -- 打印1999
f1()        -- 打印1999

  执行完t = Test(1979)后,Test的局部变量n就结束生命周期了,所以当f1,f2这两个闭包被创建时堆栈上根本找不到变量n。Test函数的局部变量n不仅是foo的upvalue,也是inner1和inner2的upvalue。t = Test(1979)之后,闭包t  已经把n保存为upvalue,之后f1、f2如果在当前堆栈上找不到变量n就会自动到它们的外包闭包(这里是t的)的upvalue引用数组中去找.

g1和g2与f1和f2共享同一个upvalue。因为g1和g2与f1和f2都是同一个闭包t 创建的,所以它们引用的upvalue  (变量n)实际也是同一个变量,而它们的upvalue引用都会指向同一个地方。

Lua的upvalue和闭包相关推荐

  1. lua学习笔记之闭包

    Lua中的函数是具有适当词法范围的一级值. lua作为一级值意味着函数与数字和字符串一样具有值,可以将函数存储在变量或者表中,将函数作为参数传递给其它函数,也可以作为结果返回函数. lua具有词法范围 ...

  2. lua 函数调用1 -- 闭包详解和C调用

    这里, 简单的记录一下lua中闭包的知识和C闭包调用 前提知识: 在lua api小记2中已经分析了lua中值的结构, 是一个 TValue{value, tt}组合, 如果有疑问, 可以去看一下 一 ...

  3. Lua 中的 function、closure、upvalue

    Lua 中的 function.closure.upvalue function,local,upvalue,closure 参考: Lua基础 语句 lua学习笔记之Lua的function.clo ...

  4. 深入理解Lua的闭包:概念和应用

    深入理解Lua的闭包一:概念和应用_cbbbc的博客-CSDN博客 本文首先通过具体的例子讲解了Lua中闭包的概念,然后总结了闭包的应用场合,最后探讨了Lua中闭包的实现原理. 闭包的概念 在Lua中 ...

  5. lua中的bind函数,闭包函数,终于知道有啥用处了

    -- 闭包绑定 function Bind(self, func, ...)assert(self == nil or type(self) == "table")assert(f ...

  6. lua的closure创建和使用

    lua的闭包包括CClosure和FClosure两种类型.下面的例子介绍如何在C中使用C创建的闭包函数,C中使用lua中创建的闭包函数,Lua中使用C中创建的闭包函数,Lua中使用Lua闭包就不赘述 ...

  7. 深入理解 Lua 虚拟机

    作者:nicochen,腾讯 IEG 游戏开发工程师 本文从一个简单示例入手,详细讲解 Lua 字节码文件的存储结构及各字段含义,进而引出 Lua 虚拟机指令集和运行时的核心数据结构 Lua Stat ...

  8. Lua 入门详情讲解

    一.Lua的介绍 Lua 语音是由巴西里约热内卢天主教大学 ([Pontifical Catholic University of Rio de janeiro ) 里的一个研究小组与 1993年开发 ...

  9. 【游戏客户端面试题干货】--2021年最新游戏客户端面试干货(lua篇)

      [游戏客户端面试题干货]-- 2021年度最新游戏客户端面试干货(lua篇)     大家好,我是Lampard~~   经过春招一番艰苦奋战之后,我终于是进入了心仪的公司.   今天给大家分享一 ...

最新文章

  1. 你真的会玩SQL吗?Case的用法(转)
  2. 深度学习中 batchnorm 层是咋回事?
  3. Linux更换python版本 (转载)
  4. 如何系统性掌握深度学习中的数据使用
  5. SpringBoot中的Tomcat是如何启动的
  6. 三元运算符和if else_PHP If-Else,Switch Case和速记三元运算符示例
  7. gvim 常用命令
  8. C#:在u3d中操作sqlite的数据库
  9. volatility内存取证
  10. java专用英语词典软件_英语词典app哪个好 5款好用的英语词典app推荐
  11. Python破解zip文件解压密码
  12. is_enabled:selenium中判断元素是否可以使用;is_selected()/is_displayed()
  13. 网页导出pdf不完整_怎么把pdf文件导出为图片?支持导出什么图片格式?
  14. ubuntu下deactivate matlab的操作
  15. 开发随笔——花生壳错误“您的局域网服务器连接失败,请检查局域网IP与端口“
  16. 阿里云自建k8s存储插件csi安装使用
  17. 【鲲鹏HCIA考试】随堂习题卷二
  18. python中字母大小顺序_Python中的字母顺序
  19. 磁盘大于16TB如何做ext4的文件系统
  20. python第三章课后答案_XX医学院本科各专业《Python》第三章习题与答案-2020年实用精品...

热门文章

  1. Supplier接口练习之获取最大值
  2. aop简介-aop的底层实现
  3. IDEA的常用快捷键
  4. 数据库-优化-慢查日志分析工具-pt-query-digest介绍及作用
  5. 请求参数绑定集合类型
  6. 服务容错和Hystrix
  7. SpringBoot_配置-@ConfigurationProperties与@Value区别
  8. iostat命令(转)
  9. Linux文件系统之swap
  10. 短视频自研还是选择第三方?技术选型前必看的自检清单