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

感谢前人的分享 :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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Lua 5.1 参考手册 by Roberto Ierusalimschy, Luiz Henrique de F...
    苏黎九歌阅读 13,517评论 0 38
  • 函数有两种用途: 完成指定任务,此时函数作为调用语句使用。 计算并返回值,此时函数作为赋值语句的表达式使用。 调用...
    JunChow520阅读 3,622评论 0 3
  • 第一篇 语言 第0章 序言 Lua仅让你用少量的代码解决关键问题。 Lua所提供的机制是C不擅长的:高级语言,动态...
    testfor阅读 2,562评论 1 7
  • 我哭了,我激动地咳嗽了,醒来我也咳嗽,就这样咳嗽醒了。5点半醒了,也不再睡觉了。不过确实有点困。 那么是什么梦让我...
    野地百合_35b6阅读 282评论 2 6
  • 从用户输入url按下回车到页面加载显示的主要流程包括: 1、浏览器的地址栏输入URL并按下回车(域名结构)。2、浏...
    难得还记得阅读 115评论 0 0