Lua学习系列-概述

Lua其语言

“Lua 是一个小巧的脚本语言。是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个研究小组,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所组成并于1993年开发。 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。”[1]

玩过WOW的人也许对lua并不陌生,著名的WOW大脚插件便是基于lua写的。

lua的设计理念足够小巧跟专注,所以它只实现了本语言的必要脚本逻辑功能,类似IO库、CGI库等的库是不存在的,由此决定了绝大多数情况下,它只适合作为嵌入式语言来完成逻辑,而不能像php、python一样,可以直接独立完成一个应用程序。

Lua 的位置

语言层级

语言描述
脚本即Lua所在层,跑在高级语言层的解释器,会对脚本逻辑进行运行时解释
高级语言比如C++、C#等,经过gcc或者vs等编译器,链接、编译为汇编程序
汇编语言比如masm。汇编语言已经可以直接操作内存地址、CPU寄存器等,非常底层而不会有高级语言的类型检查等高阶功能
操作系统各类硬件的driver都属此列。操作系统将汇编转为机器能读的机器语言
机器语言硬件厂商会刷进硬件的一些固化的操作。机器语言比汇编更加细致地进行硬件级操作,这也是普通人能接触到的最底层的部分了
微指令机器语言到微指令,类似于汇编到机器码的关系 ,它其实是外界的一种合理猜测,此层的真实面目未知(个人没有见到)
机器码010101010…

一般应用框架

以服务端举例,一些拥有相对完备的库的语言,一般可以采用类似的框架设计:
服务器架构例子

lua的作为轻量级定位的“嵌入式”语言,目前拥有的库较为简单,没有办法采用类似上述的框架,而有当下有常见的、另外两种用法。

依靠其他语言为底层,作为比其他语言更高层级的纯逻辑层:
在这里插入图片描述
服务器框架中,skynet以及我们的项目便是此种用法。

或者再退化一点,外挂在其他高级语言上,作为一种辅助部分(可以是配置读取工具,可以是比如活动等部分简单逻辑):
在这里插入图片描述
也常见于其他的服务端框架。

Lua的语法

lua 的语法较为简单且严谨,在后续的学习中会详细了解到。
同时可以参阅更为详细周到的、lua中文翻译的《programing in Lua》

代码块

lua里代码块称之chunk。在C/C++中,chunk用{}花括号来表示,在python中,chunk用行的缩进表示。在lua中,chunk使用 end 关键字来匹配表示。比如

function foo()
	-- function foo
end

if a > b then
-- do something
end

强语言、弱语言

强类型语言,如c语言,会要求变量在使用前声明其类型。

char c = "a";

lua并不要求对变量的类型声明,变量只有“tag”作用,运行期间的值类型是可变的,属于弱语言。

local c = "a"
c = 1

流程控制

判断流程,使用关键字 if else elseif
循环流程,有关键字 for while repeat
错误跳转,有 pcall、xpcall 关键字可以使用
函数执行,使用 function 关键字

内存管理

lua使用的内存会在超出其作用域之后,自动释放。自动释放内存的过程叫gc(garbage collect)。而并不需要显式的 alloc 以及 free。

gc通过一种叫marked_tree(给树涂色)的算法来进行。如果需要回收的对象过多,会导致gc树的规模庞大,而引起可能的程序卡顿。所以在使用lua时,应当注意避免频繁的临时变量产生以及过多的小对象申请。

面向对象

lua并没有提供原生的面向对象的语法。但有metatable功能提供,可以利用它来实现具体的面向对象功能。但个人建议,不要在lua中大量使用面向对象,而是多使用组合(combine)。

多文件

lua使用require关键字,来对分处不同文件的代码进行管理。进而实现lua的模块化。

数据类型

数值型

lua无分浮点型跟整型,所有的数字都是“数值型”。“数值型”在底层会自动适应浮点型和整型。

local num = 1.0001

字符串型

lua的字符串分长字符串跟短字符串(界限长度由编译的宏定义决定,5.2.5版本此值默认为长度20)。短字符串在内部会进行“字符串内部化”,换句话说就是短字符串在整个lua实例里都是对字符串唯一实例的引用。

local str = "test"

闭包型

也就是lua的“函数”类型。严格来说,lua没有直接暴露出真正的函数,所有的认识上的“函数”,其实都是闭包,而函数则是闭包下的原型(proto)。

local func = function()

end

table型

table的使用非常简便,它像是 vector + hash 的结合体。根据不同的使用场景,table的底层数据存储结构,会自动适应数组或者是哈希又或者是两者的结合体。这在后面会详细去论证。

-- 数组用法
local array = {}
table.insert(array, 1)

-- 哈希用法
local hash = {}
hash["a"] = 1
hash.b = 2

table还有一种进阶的特殊形式,弱表(weak table)。用以自动释放已经无引用的键值或者元素。
另外table还能设置自己的元表(meta table),提供对自己访问的控制能力。

nil型

lua并没有delete操作,而是使用nil值来进行“删除”。

local tb = { a = 2 }
tb.a = nil -- 刪除 a (将a置空)

当然由于这种特殊的操作,有时候置为nil,跟原本就不存在,是有一定的区别的。

boolean型

true or false

local b = true

userdata型

这是C/C++等底层所创建出来,用来供lua引用使用的结构。lua本身并不会产生userdata。

thread型

也就是协程(Coroutine)。lua并没有真正的thread,而是使用了协程的概念。

可以这么理解:传统的 thread 编程,是“多核多线程”,协程则是“单核多线程”。在同一时间,只会有一个协程在执行,直到运行的协程换出。

这样的好处是能够做到一定程度的异步(比如在一个协程读数据库换出时,另一个协程可以被唤醒做其他的工作),同时也不会让写程序的人有写锁类的烦恼。

解释原理

基于寄存器的解释器和基于栈的解释器

lua从5.0版本开始,它的解释器实现转为基于寄存器的解释器(Register-based virtual machine)。

不像JVM(java虚拟机)的基于栈的解释器(Stack-based virtual machine),lua的解释器的指令可以做到更短、更有效率。

举一个简单的 1 + 2 例子。
栈解释器原理是:

top
1 <-sp
2
1001
2031
buttom

执行:
pop 出来 1
pop 出来 2
ADD 1, 2, result(3)
push result

才能得到下面的结果:

top
3 <- sp
1001
2031
buttom

而lua在基于寄存器的情况下,指令就会变得很简单,在1、2都在寄存器的情况下,直接运算出来结果并放入寄存器就行:

寄存器内容
L11
L22
L3N/A
L4N/A

指令: ADD L1, L2, L3
就可以将结果放入到 L3 中进行使用了。

lua 的栈具体的实现会在很后期,我们一起探讨。
有兴趣可以使用工具ChunkSpy来查看lua生成的虚拟机指令(Vm Instructions)。

Lua的虚拟栈(交互栈)

lua 通过栈结构来进行数据的交互(注意此栈非上文中的虚拟机栈)。
比如下面的栈中,存入了一个名字为tb的table结构

local tb = 
{
	a_key = "a_val"
}
栈内容正索引负索引
tb2-1
other1-2

需要取出 tb.a 的值,并压栈,那么过程是
1、先将 a_key 字符串(key)压栈

栈内容正索引负索引
“a_key”3-1
tb2-2
other1-3

2、调用函数,读出在 -2 中的表,并pop -1 的key值,解析table获取到val值,再将val值重新放入到栈顶

栈内容正索引负索引
“a_val”3-1
tb2-2
other1-3

上面的栈操作过程,就是lua源码中的 lua_gettable 函数。

总结

至此,我已经描绘了lua的大概轮廓。更加深入详细的信息,从lua的基本语法,到源码实现,我会一点一点进行总结,观众老爷们可以关注我的更新,跟我一起探讨。

ciao!

[1] 百度百科

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值