GVKun编程网logo

关于lua的闭包(Closure)和Upvalue(lua中的闭包)

24

在本文中,我们将给您介绍关于关于lua的闭包的详细内容,并且为您解答Closure和Upvalue的相关问题,此外,我们还将为您提供关于closurePHP闭包(Closure)初探、luacclos

在本文中,我们将给您介绍关于关于lua的闭包的详细内容,并且为您解答Closure和Upvalue的相关问题,此外,我们还将为您提供关于closure PHP闭包(Closure)初探、lua cclosure 的 upvalue 数量限制(转自云风的blog)、LUA中的闭包(closure)浅析、lua学习笔记之Lua的function、closure和upvalue的知识。

本文目录一览:

关于lua的闭包(Closure)和Upvalue(lua中的闭包)

关于lua的闭包(Closure)和Upvalue(lua中的闭包)

关于lua的闭包(Closure)和Upvalue

upvalue:嵌套函数的外部函数的局部变量
function func(a) <== 这个函数返回值是一个函数
return function ()
    a = a + 1    <== 这里可以访问外部函数func的局部变量a,这个变量a就是upvalue
    return a 
end
end

func返回一个匿名函数,可用变量接取之。该匿名函数有一个upvalue a(有点像C函数的static变量),初值为首次调用func时的参数

闭包:一个匿名函数加上其可访问的upvalue
c = func(1) <== c现在指向一个拥有upvalue a = 1的匿名函数,c也被称作一个闭包
c()          <== 返回2
c()          <== 返回3
c2 = func(1) <== c2现在指向另外一个拥有upvalue a = 1的匿名函数,c2也被称作一个闭包
c2()         <== 返回2

下面是示例代码:

int 
generatecclosure(lua_State *L)        /* 闭包产生器 */
{
    lua_pushnumber(L,0);             /* 压入第一个upvalue */ 
    lua_pushnumber(L,0);             /* 压入第二个upvalue */
    lua_pushcclosure(L,cclosure,2); /* 压入闭包的同时也把upvalue置入该闭包的upvalue表 */
    return 1;                         /* 返回闭包 */
}

int 
cclosure(lua_State *L)
{
    double upval1,upval2;
    upval1 = lua_tonumber(L,lua_upvalueindex(1)); /* 注意upvalue索引1,2是闭包依赖的,不会和其他的闭包中的索引冲突 */
    upval2 = lua_tonumber(L,lua_upvalueindex(2));
    upval1++; upval2++;    
    lua_pushnumber(L,upval1); lua_replace(L,lua_upvalueindex(1));/* 更新upvalue1 */
    lua_pushnumber(L,upval2); lua_replace(L,lua_upvalueindex(2));/* 更新upvalue2 */
    lua_pushnumber(L,upval1 + upval2);
    return 1;
}

int 
generatecclosure2(lua_State *L)        
{
    lua_pushnumber(L,10);             
    lua_pushnumber(L,10);             
    lua_pushcclosure(L,cclosure2,2); 
    return 1;                          
}

int 
cclosure2(lua_State *L)
{
    double upval1,lua_upvalueindex(1));
    upval2 = lua_tonumber(L,lua_upvalueindex(2));
    upval1++; upval2++;
    lua_pushnumber(L,lua_upvalueindex(1));
    lua_pushnumber(L,lua_upvalueindex(2));
    lua_pushnumber(L,upval1 + upval2);
    return 1;
}

然后向lua虚拟机注册一下全局函数:

lua_register(L,"generatecclosure",generatecclosure);
lua_register(L,"generatecclosure2",generatecclosure2);

最后执行main.lua:

c = generatecclosure() c2 = generatecclosure2() print(c()) -- 2 print(c()) -- 4 print(c2()) -- 22 print(c2()) -- 24

closure PHP闭包(Closure)初探

closure PHP闭包(Closure)初探

lua cclosure 的 upvalue 数量限制(转自云风的blog)

lua cclosure 的 upvalue 数量限制(转自云风的blog)

总结

以上是小编为你收集整理的lua cclosure 的 upvalue 数量限制(转自云风的blog)全部内容。

如果觉得小编网站内容还不错,欢迎将小编网站推荐给好友。

LUA中的闭包(closure)浅析

LUA中的闭包(closure)浅析

之前对closure一知半解,在网上也找不到一篇文章能把它说清楚,今天好像第一次对它有点清晰的了解 了,写个BLOG记念一下

lua的函数是一种 First-Class Value 的东西, 到底是啥?
就是它们与传统类型的变值没啥区别,
可以 存到一个变量中,
可以 存到table中,
可以 作为实参传递给其它函数,
可以 作为其它函数的返回值.

它们还具有特定的词法域(Lexical Scoping), 也就是说, 一个函数可以嵌套在另一个函数中, 内部的函数可以访问外部函数中的变量.
如下面的例子:

复制代码 代码如下:

function test(x)
    return function (value)
        return value * x
    end
end

func = test(10)

print( func(11) )


在test()中,嵌套了一个匿名函数作为返回值, 而在这个匿名函数中 可以访问外部的 value 变量
再看另一个例子
复制代码 代码如下:

function newCounter()
        local i = 0
        func = function()
                i = i + 1
                return i
        end

        return func
end

c = newCounter()
print(c())
print(c())

c1 = newCounter()
print(c1())
print(c1())


代码中, 函数 func 里访问了一个 "非局部的变量" i, 用于保存一个计数器
初步看,由于创建变量i的函数 newCounter 已经返回, 所以每次调用 func 时, 应该是超过了作用范围

其实不然, lua 会以 closure 的概念来处理这种情况.
一个 closure 就是一个函数加上该函数所需访问的所有"非局部的变量"

所以上例中 c1, c2 是同个函数所创建的两个不同的 closure, 它们各自拥有局部变量i的独立实例.

从技术上来讲, lua中只有 closure, 而不存在"函数". 因为"函数"本身就是一种特殊的 closure.

后记,C++的类对象不也可以达到类似的效果?

您可能感兴趣的文章:
  • Lua学习笔记之表和函数
  • Lua进阶教程之闭包函数、元表实例介绍
  • Lua基础教程之赋值语句、表达式、流程控制、函数学习笔记
  • Lua中的闭包小结
  • Lua中的闭包学习笔记
  • Lua学习笔记之函数、变长参数、closure(闭包)、select等
  • lua闭包的理解以及表与函数的几种表达方法

lua学习笔记之Lua的function、closure和upvalue

lua学习笔记之Lua的function、closure和upvalue

Lua中的函数是一阶类型值(first-class value),定义函数就象创建普通类型值一样(只不过函数类型值的数据主要是一条条指令而已),所以在函数体中仍然可以定义函数。假设函数f2定义在函数f1中,那么就称f2为f1的内嵌(inner)函数,f1为f2的外包(enclosing)函数,外包和内嵌都具有传递性,即f2的内嵌必然是f1的内嵌,而f1的外包也一定是f2的外包。内嵌函数可以访问外包函数已经创建的所有局部变量,这种特性便是所谓的词法定界(lexical scoping),而这些局部变量则称为该内嵌函数的外部局部变量(external local variable)或者upvalue(这个词多少会让人产生误解,因为upvalue实际指的是变量而不是值)。试看如下代码:

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

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

  可为什么g2与g1的函数体一样(都是f1的内嵌函数f2的函数体),但打印值不同?这就涉及到一个相当重要的概念——闭包(closure)。事实上,Lua编译一个函数时,会为它生成一个原型(prototype),其中包含了函数体对应的虚拟机指令、函数用到的常量值(数,文本字符串等等)和一些调试信息。在运行时,每当Lua执行一个形如function...end 这样的表达式时,它就会创建一个新的数据对象,其中包含了相应函数原型的引用、环境(environment,用来查找全局变量的表)的引用以及一个由所有upvalue引用组成的数组,而这个数据对象就称为闭包。由此可见,函数是编译期概念,是静态的,而闭包是运行期概念,是动态的。g1和g2的值严格来说不是函数而是闭包,并且是两个不相同的闭包,而每个闭包可以保有自己的upvalue值,所以g1和g2打印出的结果当然就不一样了。虽然闭包和函数是本质不同的概念,但为了方便,且在不引起混淆的情况下,我们对它们不做区分。

  使用upvalue很方便,但它们的语义也很微妙,需要引起注意。比如将f1函数改成:

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

  内嵌函数定义在n = n + 10这条语句之前,可为什么g1()打印出的却是1989?upvalue实际是局部变量,而局部变量是保存在函数堆栈框架上(stack frame)的,所以只要upvalue还没有离开自己的作用域,它就一直生存在函数堆栈上。这种情况下,闭包将通过指向堆栈上的upvalue的引用来访问它们,一旦upvalue即将离开自己的作用域(这也意味着它马上要从堆栈中消失),闭包就会为它分配空间并保存当前的值,以后便可通过指向新分配空间的引用来访问该upvalue。当执行到f1(1979)的n = n + 10时,闭包已经创建了,但是n并没有离开作用域,所以闭包仍然引用堆栈上的n,当return f2完成时,n即将结束生命,此时闭包便将n(已经是1989了)复制到自己管理的空间中以便将来访问。弄清楚了内部的秘密后,运行结果就不难解释了。

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

 
 
  function Create(n)   local function foo1()   print(n)   end   local function foo2()   n = n + 10   end    return foo1,foo2   end   f1,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的这种语义很有价值,它使得闭包之间可以不依赖全局变量进行通讯,从而使代码的可靠性大大提高。

  闭包在创建之时其upvalue就已经不在堆栈上的情况也有可能发生,这是因为内嵌函数可以引用更外层外包函数的局部变量:

 
 
  function Test(n)   local function foo()   local function inner1()   print(n)   end   local function inner2()   n = n + 10   end    return inner1,inner2   end    return foo   end   t = Test( 1979 )   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的踪影,这叫它们如何取得n的值呢?呵呵,不要忘了Test函数的n不仅仅是inner1和inner2的upvalue,同时它也是foo的upvalue。t = Test(1979)之后,t这个闭包一定已经把n妥善保存好了,之后f1、f2如果在当前堆栈上找不到n就会自动到它们的外包闭包(姑且这么叫)的upvalue引用数组中去找,并把找到的引用值拷贝到自己的upvalue引用数组中。仔细观察上述代码,可以判定g1和g2与f1和f2共享同一个upvalue。这是为什么呢?其实,g1和g2与f1和f2都是同一个闭包(t)创建的,所以它们引用的upvalue(n)实际也是同一个变量,而刚才描述的搜索机制则保证了最后它们的upvalue引用都会指向同一个地方。

  Lua将函数做为基本类型值并支持词法定界的特性使得语言具有强大的抽象能力。而透彻认识函数、闭包和upvalue将帮助程序员善用这种能力。

我们今天的关于关于lua的闭包Closure和Upvalue的分享已经告一段落,感谢您的关注,如果您想了解更多关于closure PHP闭包(Closure)初探、lua cclosure 的 upvalue 数量限制(转自云风的blog)、LUA中的闭包(closure)浅析、lua学习笔记之Lua的function、closure和upvalue的相关信息,请在本站查询。

本文标签:

上一篇LuaJavaBridge - Lua 与 Java 互操作的简单解决方案(lua和java)

下一篇Lua的function、closure和upvalue(lua function)