`

lua 5.0的实现(翻译)4,5

阅读更多

4

    Tablelua的主要——实际上,也是唯一的——数据结构。Table不仅在语言中,同时也在语言的实现中扮演着重要角色。Effort spent on a good implementation of tables is rewarded in the language,because tables are used for several internal tasks, with no qualms about performance。这有助于保持实现的小巧。相反的,任何其他数据结构的机制的缺乏也为table的高效实现带来了很大压力

       Lua中的table是关联数组,也就是可以用任何值做索引(除了nil),也可以持有任何值。另外,table是动态的,也就是说当加进数据的时候它们将增长(给迄今不存在的域赋值)而移除数据的时候将萎缩(给域赋nil值)。

不像大多数其他脚本语言,lua没有数组类型。数组被表示为以整数做键的table。用table作为数组对于语言是有益的。主要的(益处)显而易见:lua并不需要操纵表和数组的两套不同的运算符。另外,程序员不用对两种实现做出艰难选择。在lua中实现稀疏数组是轻而易举的。例如,在Perl里面,如果你尝试去跑$a[1000000000]=1 这样的程序,你能跑出个内存溢出!(you can run out of memory),因为它触发了一个10亿个元素的数组的创建(译注,Perl的哲学是:去除不必要的限制)。而等价的lua程序 a={[1000000000]=1},(只是)创建了有一个单独的项的表(而已)。

<!----><!---->
<!---->

直到lua 4.0table都是作为hash表严格地实现:所有的pair都被显式地保存。Lua5.0引入了一个新算法来优化作为数组的table:它将以整数为键的pair不再是存储键而是优化成存储值在真正的数组中。更精确地说,在lua 5.0中,table被实现为一个混合数据结构:它们包括一个hash部分和一个数组部分。图2展示了一个有"x" 9.3, 1 100,2 200, 3 300对子的表的一种可能结构。注意到数组部分在右边,并没有存储整数的key。这个划分仅仅是在一个低的实现层次进行的,访问table仍然是透明的,甚至在虚拟机内部(访问table)也是如此。Table根据内容自动并且动态地对两个部分进行适配:数组部分试图从1n的相应地存储值,关联非整数键或者整数键超过数组范围的值被存储在hash部分。

当一个table需要增长时,lua重新计算hash部分和数组部分的大小。任一部分都可能是空的。重新计算后的数组大小是至少是当前使用的数组部分从1n的一半情况下的最大n值(原文:The computed size of the array part is the largest n such that at least half the slots between 1 and n are in use)(稀疏数组时避免浪费空间),并至少有一个(元素)处在n/2+1n的槽中(当n2整除时,避免n的这样的数组大小)。计算完大小后,lua创建了“新”的部分并将“旧”的部分的元素重新插入到的“新”的部分。作为一个例子,假设a是一个空表;它的数组部分和hash部分的大小都是0。当我们执行a[1]=v时,这个表需要增长到足够容纳新的键。Lua将为新的数组部分的大小选择n=1(带有一个项1v)。hash部分仍然保持为空。

这个混合的方案有两个优点。首先,访问以整数为键的值加快了,因为不再需要任何的散列行为。其次,也是最重要的,数组部分占用的内存大概是将数组部分存储在hash部分时的一半,因为key在数组中是隐式的(译注:也就是数组的下标)而在hash部分却是显式的。因而,当一个table被用作数组的时,它表现的就像一个数组,只要整数键是密集。另外,不用为hash部分付出任何内存和时间上的惩罚,因为它(译注:hash部分)甚至都不存在。相反的控制:如果table被用作关联数组而非一个数组,数组部分可能就是空的。这些内存上的节省是比较重要的,因为lua程序经常创建一些小的table,例如table被用来实现对象(Object)(译注,也就是用table来模仿对象,有点类似javascript中的json)。

Hash部分采用Brent's variation[3]的组合的链状发散表。这些table的主要不变式是如果一个元素没有在它的主要位置上(也就是hash值的原始位置),则冲突的元素在它自己的主要位置上。换句话说,仅当两个元素有相同的主要位置(也就是在当时table大小情况下的hash值)时才有冲突的。没有二级冲突。正因为那样,这些table的加载因子可以是100%而没有性能上的损失。这部分不是很明白,附上原文:

The hash part uses a mix of chained scatter table with Brent's variation [3].

A main invariant of these tables is that if an element is not in its main position

(i.e., the original position given by its hash value), then the colliding element

is in its own main position. In other words, there are collisions only when two

elements have the same main position (i.e., the same hash values for that table

size). There are no secondary collisions. Because of that, the load factor of these

tables can be 100% without performance penalties.

 

 

5、函数和闭包

lua编译一个函数的时候,它产生一个模型(prototype),包括了函数的虚拟机指令、常量值(数字,字符串字面量等)和一些调试信息。运行的时候,无论何时lua执行一个function…end表达式,它都创建一个新的闭包。每个闭包都有一个引用指向相应的模型,一个引用指向环境(environment)(一张查找全局变量的表,译注:指所谓环境就是这样一张表),和一组用来访问外部local变量的指向upvalue的引用。

词法范围以及first-class函数的组合导致一个关于(如何)访问外部local变量的著名难题。考虑图3中的例子。当add2被调用的时候,它的函数体(body)部分引用了外部local变量x (函数参数在lua里是local变量,译注:x就是所谓的自由变量,这里形成了闭包)。尽管如此,当add2被调用时,生成add2的函数add已经返回。如果在栈中生成变量x,(x在栈的)其栈槽将不复存在。(译注,此处的意思应该是说如果在栈保存变量x,那么在调用add2的时候,add函数早已经返回,x也在add2调用前就不在栈里头了,这就是那个著名难题)。

<!----><!---->
<!---->

 

 

大多数过程语言通过限制词法范围(例如python),不提供first-class函数(例如Pascal),或者都两者都采用(例如c,译注:也就是说c既不把函数当一等公民,也限制词法范围)来回避这个问题。函数式语言就没有那些限制。围绕着非纯粹函数语言比如SchemeML的研究已经产生了大量的关于闭包的编译技术的知识(参见[19, 1, 21])。尽管如此,这些工作并没有尽力去限制编译器的复杂性。以优化的Scheme编译器Bigloo的控制流分


析阶段为例,(它的实现)比lua实现的10倍还大:Bigloo 2.6fCfa模块源码有106,350 VS. 10,155行的lua5.0核心。正如第2部分已经解释过的(原因),lua需要简单。<!----><!----><!---->

Lua使用了一个称为upvalue的结构来实现闭包。任何外部local变量的访问都是通过一个upvalue间接进行的。Upvalue初始指向的是变量存活位置的栈槽(参见图4的左半部分)。当变量(x)已经离开作用域(译注,也就是这里的add函数返回时),它就迁移到upvalue结构本身一个槽中(参见图4的右半部分)。因为(对变量的)访问是间接地通过upvalue结构中的一个指针进行的,因此这个迁移对于任何写或者读该变量的代码都是透明的。不像它的内嵌函数(译注:例子的add2,它是指外部函数add),声明变量的函数访问该变量就像访问它的local变量一样:直接到栈。

通过每个变量最多创建一个upvalue结构并且在必要的时候复用它们,可变状态得以在闭包之间正确地共享。为了保证这一约束,lua维持一个链表,里面是一个栈里(图4中的pending vars列表)的所有open upvalue(所谓open upvalue,是指仍然指向栈的upvalue结构)。当lua创建一个新的闭包的时候,它遍历所有的外部local变量。对于每个(外部local)变量,如果它在列表中找到一个open upvalue,那么它就复用这个upvalue结构。否则,lua就创建一个新的upvalue并将它放入链表。注意到列表搜索只是探测了少数几个节点,因为列表最多包含一个被内嵌函数使用的local变量的项。当一个closed upvalue不再被任何闭包引用的时候,它最后将被当作垃圾并回收。

一个函数允许访问一个不是它的直接外围函数的外部local变量,只要(这个local变量)是外部函数的(outer function)。在那种情况下,甚至在闭包被创建的时候,(外部local)变量可能就不在栈里了。Lua使用扁平闭包(flat closures)解决这种情况[5]。通过扁平闭包,一个函数无论何时去访问一个不属于它的外围函数的外部变量,这个变量都将进入外围函数的闭包。从而当一个函数被实例化的时候,所有进入它闭包的变量要么在外围函数的栈里面,要么在外围函数的闭包里。

分享到:
评论

相关推荐

    LUA5.0 源代码

    4. **元方法和元表**:LUA5.0的元编程特性体现在元方法和元表上,允许用户自定义操作符行为。源代码`lobject.h`和`ltm.c`揭示了元表的实现细节。 5. **闭包和原型**:LUA5.0支持函数作为一等公民,闭包和原型是其...

    lua 5.0 设计实现

    《Lua 5.0 设计实现》是一本深入解析Lua编程语言核心机制的教程,尤其关注其源代码的实现细节。作为一款轻量级、高效且可嵌入的脚本语言,Lua在游戏开发、系统配置、网络编程等多个领域都有广泛应用。这本书详细介绍...

    lua5.0的实现原理剖析

    4. 协程的加入:Lua 5.0 添加了协程(Coroutine)的概念,这是一种轻量级的并发模型,允许程序在不同执行流之间切换。尽管协程的概念并不新颖,但Lua 5.0 的实现使得协程易于理解和使用,有助于提升复杂程序的并行性...

    The implementation of lua5.0

    而“新建文本文档 (2).txt”可能是对Lua 5.0实现的补充说明或者示例代码,可能包含了一些具体的应用实例,帮助读者更好地理解如何在实践中运用这些概念。 学习Lua 5.0不仅能够提升你对脚本语言的理解,还能让你掌握...

    Lua5.0中文参考

    ### Lua5.0中文参考知识点概述 #### 一、Lua简介 - **Lua**是一种轻量级、高效的脚本语言,被广泛应用于多种场景中,包括游戏开发、Web应用程序及通用脚本处理等。它的设计目标是简洁且功能强大,能够轻松地嵌入到...

    Lua 5.0 Reference Manual(Revision 1.0) - ZIP

    本翻译取材于http://www.lua.org/manual/5.0/manual.html,即《Lua 5.0 Reference Manual》之英文原版,在尽可能依照原文内容的前提下对部分艰涩的叙述进行了斟酌,以提高本书的可理解性。 原文部分章节涉及到C语言...

    Lua5.0参考手册.doc

    Lua 5.0 是一种轻量级的、通用的脚本语言,专为支持数据描述机制和一般过程式编程而设计。它具有广泛的语言特性,包括面向对象编程、函数式编程以及数据驱动编程的支持,使其成为强大的配置语言。Lua 语言的实现是一...

    Reference manual for Lua 5.0

    ### Lua 5.0 参考手册知识点概览 #### 一、Lua 5.0 简介 - **版本信息**:本手册为 Lua 5.0 的参考手册,最后一次修订日期为 2003 年 11 月 25 日。 - **版权信息**:该文档由 Tecgraf, PUC-Rio 版权所有,但允许...

    Lua 5.0 Reference Manual(Revision 1.0) - PDF

    ### Lua 5.0 参考手册核心知识点总结 #### 一、绪论与背景介绍 - **Lua语言概述**:Lua是一种轻量级、高效的脚本语言,它以其简洁性和优雅性闻名。Lua的设计原则之一是保持轻巧,这使得它的标准库相比其他脚本语言...

    lua 5 实现1

    【Lua 5 实现1】文章探讨了 Lua 5.0 的主要改进,这些改进使得 Lua 更加适合工业级应用,特别是在游戏开发领域。以下是详细的知识点解析: 1. **基于寄存器的虚拟机**:传统的虚拟机,如Java虚拟机(JVM),基于堆栈...

    lua实现机制

    一个重要的例子是Lua5.0版本中引入的协同程序(coroutines),以及在即将到来的Lua5.1版本中实现的增量式垃圾回收机制。这两项功能对于游戏开发尤为重要。 Lua5.0的主要创新包括基于寄存器的虚拟机、用于优化作为...

    the implementation of lua 5p0

    《Lua 5.0 实现的关键创新》 一、引言与背景 Lua,自1993年在巴西的学术实验室诞生以来,迅速成为全球软件开发,尤其是游戏产业中的重要脚本语言。其设计与实现的目标始终围绕着提供一个简单、高效、可移植且轻量...

    Lua 的实现,Lua使用者不能不看,脚本语言的经典啊。

    ### Lua 5.0 实现的关键特性解析 #### 引言 Lua 作为一种轻量级、高效的脚本语言,自1993年诞生以来,迅速在游戏开发及其他多个行业中得到了广泛应用。它之所以能获得如此高的认可度,很大程度上归功于其设计目标:...

    lua-5.1.5.0

    `lua-5.1.5.0`是Lua语言的一个版本,发布于2009年,它在Lua 5.1系列中是一个稳定且广泛使用的版本。 1. **语言特性** - **简单语法**:Lua语法简洁明了,基于C语言,但更加面向表达式,使用括号较少,易于阅读和...

    lua编译&反编译,lua反编译工具,Java源码.zip

    Lua的官方工具luac可以实现这一目标。luac将Lua源代码编译为字节码,并写入到一个.lua.out文件中,这是一个二进制文件。预编译后的代码可以更快地加载和执行,因为它避免了运行时的解释过程。 反编译是将已编译的...

    luaEditor5.0

    luaEditor5.0发布 1. 优化内核,极大提高了调用速度 2. 添加搜索框,智能动态线程搜索,搜索速度很快 3. 高亮标记搜索结果 4. 添加智能是否需要+end功能 5. 利用短点当书签功能 6. 添加设置保存,遗迹可以设置背景色...

    所有版本LUA源码

    所有版本LUA源码 lua-5.3.5 lua-5.3.4 lua-5.3.3 lua-5.3.2 lua-5.3.1 ...lua-5.0 lua-4.0.1 lua-4.0 lua-3.2.2 lua-3.2.1 lua-3.2 lua-3.1 lua-3.0 lua-2.5 lua-2.4 lua-2.2 lua-2.1 lua-1.1 lua-1.0

    lua设计模式总结

    4、建造者模式lua实现 5、单例模式lua实现 6、抽象工厂模式lua实现 结构型模式 1、装饰模式lua实现 2、代理模式lua实现 3、外观模式lua实现 4、适配器模式lua实现 5、组合模式lua实现 6、桥接模式lua实现 7、享元...

    Lua 中实现类

    5. 利用多态性实现不同类对象的差异化行为。 通过这些技巧,我们可以在Lua中灵活地实现面向对象编程,尽管它不是原生支持的。这种灵活性也使得Lua成为游戏开发和其他需要轻量级脚本语言的场景中的首选工具。

Global site tag (gtag.js) - Google Analytics