lua进阶笔记1:函数与闭包

2019-11-16  本文已影响0人  Charon_ted

感谢前人的分享 :Lua的upvalue和闭包

首先我们先来举一个c++中函数的例子,我们声明了一个函数,例如

void printTest()
{
  cout<<"just print";
}

这里函数名printTest实质上存放的是该函数的函数地址。然后我们再开看Lua中的函数,看看有什么区别。


然后我们再来看lua。lua中的函数有三点需要注意的地方:

1.在Lua语言中,函数是第一类值。这意味着函数和其他类型(例如 int float)是相同的,你既可以把他当作函数的参数传入 也可以当作返回值传出。

2.在lua中,所有函数都是匿名的。像其他所有值一样,函数并没有名字。当讨论函数名时,比如 print,实际上值得是保存该函数的变量。

3.从技术上面讲,lua中其实只有闭包没有函数。函数本身只是闭包的一种原型。

这里前两点比较好理解我们跳过着重讲一下第三点。那么什么事闭包?先看个简单地示例

function newCounter()
  local count=0
  return function()
            count = count + 1 
            return count
          end
end

c1 = newCounter()
print(c1())    -->  1
print(c1())    -->  2
c2 = newCounter()
print(c2())    -->  1

这里我们发现了一个很有意思的现象。就是在newCounter中,count的值被保存了下来。其根本原因是因为在这里,count其实是一个 上值(upvalue)也叫作非局部变量(non-local variable)

在这里我们有函数newCounter和一个匿名函数记作f。其中fnewCounter内嵌函数newCounterf外包函数。这两种性质同样具有传递性。即f的内嵌函数同时也是newCounter的内嵌函数,newCounter的外包函数同时也是f的外包函数。而被内嵌函数访问的外包函数中的变量,便是该内嵌函数的上值

这里我们搞清楚了什么上值,但上值又是怎么保存下来的呢?这就关系到我们上面所说的第三点:闭包

lua编译一个函数的时候,其中办函了函数体对应的虚拟机指令、函数用到的常量值(数、字符串等等)和一些调试信息。在运行时,每当lua执行一个形如function...end 这样的函数是,它就会创建一个新的数据对象,其中包含了响应函数原型的引用、环境(用来查找全局变量、方法的表)的引用以及一个有所有upvalue数据组成的数组,而这个数据对象就成为闭包

我们就会发现,其实在lua中,如开头所举的c++中函数只是编译期的概念。而在真正运行的时候实质上都是闭包。而上面例子中的c1、c2严格来说不是函数而是闭包。而且c1、c2分别是两个不同的闭包,他们有着各自的upvalue值所以每次打印出来的值都被分别保存了下来。

而关于upvalue,他的实质其实是局部变量,而局部变量是保存在函数堆栈框架上的。所以只要upvalue还没有离开自己的作用域,他就一直生存在函数堆栈上。这时闭包通过指向堆栈上upvalue的引用来方位他们。而一旦upvalue离开了自己的作用域,在被堆栈消除之前,闭包就会为它分配空间并保存当前的值,以后便可以通过指向新分配的空间的引用来访问upvalue


upvalue和闭包数据共享

在我们对闭包和upvalue的概念有所了解后我们来看两种用法

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

function newCounter()
    local count = 0
    
    function  f1()
        count = count + 1
        return count
    end
    function  f2()
        print(count)
    end

    return f1,f2
end

g1 , g2 = newCounter()

g1()
g2()    -->1

g1()
g2()    -->2

g1、g2两个闭包的原型分别是f1、f2两个函数,而这两个闭包同时指向了一个相同的upvalue : count。在局部变量的作用域结束的时候,系统发现g1 g2两个闭包分别指向了相同的 upvalue,系统便只生成了 一个拷贝供两个闭包共同使用。此时任意一个闭包对该数据进行操作都会影响到另一个闭包。而这种操作的优点在于两个闭包之间可以不依赖于全局变量进行通信,并且该 upvalue也相对较安全。

2.多重内嵌函数的闭包 (闭包创建的闭包)
同一闭包创建的其他闭包共享同一份upvalue
先上代码

function newCounter()
    local count = 0
    function f0()
        function  f1()
            count = count + 1
            return count
        end

        function  f2()
            print(count)
        end
        return f1,f2
    end
    return f0
end

t = newCounter()

g1 , g2 = t()
g1()
g2()      --> 1

g3 , g4 = t()
g3()
g4()      --> 2

这里我们的g1 g2 g3 g4 创建的时候,count已经结束生命周期了。所以创建时堆栈上根本找不到变量count。此时他们便到他们的外包的闭包 t 中寻找。此时t g1 g2 g3 g4共享count

上一篇 下一篇

猜你喜欢

热点阅读