lua学习之闭包实现原理

感觉学习lua的过程中, 闭包的概念比较难以理解,这里记录下对闭包的学习。

闭包的概念

在Lua中,闭包(closure)是由一个函数和该函数会访问到的非局部变量(或者是upvalue)组成的,其中非局部变量(non-local variable)是指不是在局部作用范围内定义的一个变量,但同时又不是一个全局变量,主要应用在嵌套函数和匿名函数里,因此若一个闭包没有会访问的非局部变量,那么它就是通常说的函数。也就是说,在Lua中,函数是闭包一种特殊情况。在Lua中,函数是一种第一类型值(First-Class Value),它们具有特定的词法域(Lexical Scoping)。

第一类型值表示函数与其他传统类型的值(例如数字和字符串类型)具有相同的权利。即函数可以存储在变量或table。

function foo(x) print(x) end

  实质是等价于

foo = function (x) print(x) end

  因此一个函数定义实质就是一条赋值语句,这条语句创建了一种类型为“函数”的值,并赋值给一个变量。可以将表达式function (x) <body> end 视为一种函数构造式,就像table的构造式{}一样。

词法域是指一个函数可以嵌套在另一个函数中,内部的函数可以访问外部函数的变量的这样一种特征。比如:

function f1(n)
   --函数参数n也是局部变量
   local function f2()
      print(n)   --引用外部函数的局部变量
   end
   return f2
end  

g1 = f1(2015)
g1() -- 打印出2015  

g2 = f1(2016)

  

  注意这里的g1和g2的函数体相同(都是f1的内嵌函数f2的函数体),但打印值不同。这是因为创建这两个闭包时,他们都拥有局部变量n的独立实例。事实上,Lua编译一个函数时,会为他生成一个原型(prototype),其中包含了函数体对应的虚拟机指令、函数用到的常量值(数,文本字符串等等)和一些调试信息。在运行时,每当Lua执行一个形如function...end 这样的表达式时,他就会创建一个新的数据对象,其中包含了相应函数原型的引用及一个由所有upvalue引用组成的数组,而这个数据对象就称为闭包。由此可见,函数是编译期概念,是静态的,而闭包是运行期概念,是动态的。g1和g2的值严格来说不是函数而是闭包,并且是两个不相同的闭包,而每个闭包能保有自己的upvalue值,所以g1和g2打印出的结果当然就不相同了。

这里的函数f2可以访问参数n,而n是外部函数f1的局部变量。在f2中,变量n即不是全局变量也不是局部变量,将其称为一个非局部变量(non-local variable)或upvalue。upvalue实际指的是变量而不是值,这些变量可以在内部函数之间共享,即upvalue提供一种闭包之间共享数据的方法,比如:

function Create(n)
   local function foo1()
      print(n)
   end
   local function foo2()
      n = n + 10
   end
   return foo1,foo2
end  

f1,f2 = Create(2015)
f1() -- 打印2015  

f2()
f1() -- 打印2025  

f2()
f1() -- 打印2035

  注意上面的例子中,闭包f1和f2共享同一个upvalue了,这是因为当Lua发现两个闭包的upvalue指向的是当前堆栈上的相同变量时,会聪明地只生成一个拷贝,然后让这两个闭包共享该拷贝,这样任一个闭包对该upvalue进行修改都会被另一个探知。

闭包的实现原理

当Lua编译一个函数时,它会生成一个原型(prototype),原型中包括函数的虚拟机指令、函数中的常量(数值和字符串等)和一些调试信息。在任何时候只要Lua执行一个function .. end表达时,它都会创建一个新的闭包(closure)。每个闭包都有一个相应函数原型的引用以及一个数组,数组中每个元素都是一个对upvalue的引用,可以通过该数组来访问外部的局部变量(outer local variables)。值得注意的是,在Lua 5.2之前,闭包中还包括一个对环境(environment)的引用,环境实质就是一个table,函数可以在该表中索引全局变量,从Lua 5.2开始,取消了闭包中的环境,而引入一个变量_ENV来设置闭包环境。由此可见,函数是编译期概念,是静态的,而闭包是运行期概念,是动态的。

时间: 2024-08-29 15:13:55

lua学习之闭包实现原理的相关文章

Lua学习笔记(七):迭代器与泛型for

1.迭代器与闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素. 迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是他知道来自于哪里和将要前往哪里.闭包提供的机制可以很容易实现这个任务.记住:闭包是一个内部函数,它可以访问一个或者多个外部函数的外部局部变量.每次闭包的成功调用后这些外部局部变量都保存他们的值(状态).当然如果要创建一个闭包必须要创建其外部局部变量.所以一个典型的闭包的结构包含

(原创) cocos2d-x 3.0+ lua 学习和工作(4) : 公共函数(5): 返回指定表格中的所有键(key):table.keys

这里的函数主要用来做:返回指定表格中所有的键.参考资料为quick_cocos. 星月倾心贡献~~~ --[[ -- 返回指定表格中的所有键(key) -- example: local t = ( a = 1, b = 2, c = 3 ) local keys = table.keys( t ) -- keys = { "a", "b", "c" } -- @param t 要检查的表格(t表示是table) -- @param table

lua学习笔记之函数

Lua学习笔记之函数 1.  函数的作用 函数主要完成指定的任务,这样的情况下函数作为调用语句使用,函数可以计算并返回值,这样的情况下函数作为赋值语句的表达式使用. 语法: funcationfunc_name(arguments-list) Statements-list end 调用函数的时候,如果参数列表为空,必须使用()表示是函数调用. Print(8*9,9/8) a = math.sin(3) +math.cos(10) print(os.date()) 上述规则有一个例外,当函数只

(原创) cocos2d-x 3.0+ lua 学习和工作(4) : 公共函数(6): 合并表格:table.merge

这里的函数主要用来做:合并表格.参考资料为quick_cocos. 星月倾心贡献~~~ --[[ -- 将 来源表格 中所有键及值复制到 目标表格 对象中,如果存在同名键,则覆盖其值 -- example local tDest = { a = 1, b = 2 } local tSrc = { c = 3, d = 4 } table.merge( tDest, tSrc ) -- tDest = { a = 1, b = 2, c = 3, d = 4 } -- @param tDest 目

Lua学习笔记(2) —— 风格

前一篇大概学习了lua常用的语法,都是些刚入门时应该了解的.见这里 Lua学习笔记(1) ——语法 这篇主要记录一些关于lua代码风格的经验,如果不了解它们,还是拿着函数式或传统面向对象的风格去写lua,就永远无法了解lua的精华之所在,弃之所长,取其所短. 1. 函数类型的变量 2. 匿名函数 3. 闭包 4. 表 5. 协程 6. 虚拟机

(原创) cocos2d-x 3.0+ lua 学习和工作(5) : table的remove的坑

本章主要讲下,table的remove,这个东西不注意就容易被坑(被坑的飘过~~~),当然,这里是针对数组table.即用ipairs方法遍历. 星月倾心贡献~~~ 看示例1:直接nil local tbl = { 1, 2, 3, 4, 5 } for k, v in ipairs( tbl ) do if v == 3 then tbl[k] = nil else print( "one: " .. k .. ":" .. v ) end end -- 再次输出

(原创) cocos2d-x 3.0+ lua 学习和工作(4) : 公共函数(4): handler

这里的函数主要用来做:回调函数.参考资料为quick_cocos. 星月倾心贡献~~~ --[[ -- 将lua对象及方法包装为一个匿名函数 -- 许多功能需要传入一个 Lua 函数做参数,然后在特定事件发生时就会调用传入的函数.例如触摸事件.帧事件等等. -- example: local MyScene = class( "MyScene", function() ) return cc.Layer:create() end ) function MyScene:ctor() se

Lua中的闭包

[什么是闭包?] 闭包在Lua中是一个非常重要的概念,闭包是由函数和与其相关的引用环境组合而成的实体.我们再来看一段代码: function newCounter() local i = 0 return function () -- 匿名函数 i = i + 1 return i end end c1 = newCounter() print(c1()) print(c1()) 根据刚刚说的闭包的概念,结合上面的代码,来说说这个概念.闭包=函数+引用环境.上述代码中的newCounter函数返

[转]LUA 学习笔记

Lua 学习笔记 入门级 一.环境配置 方式一: 1.资源下载http://www.lua.org/download.html 2.用src中的源码创建了一个工程,注释调luac.c中main函数,生成了一个exe,直接可以测试lua了 方式二(推荐): 从https://code.google.com/p/luaforwindows/ 下载“LuaForWindows_v5.1.4-46.exe”,一键安装即可  二.执行 lua xxx.lua 三.注释 1.行注释格式:-- 2.块注释格式