April
30th,
2022
首先介绍一下 lua 语言:
它是一门动态类型语言。所谓动态就是运行时,静态就是编译时。
静态语言会在编译时做类型检查,通过报错帮助程序员发现问题。
动态语言没有编译环节,类型检查发生在运行时,查到错误就会抛出异常,
程序员需要自己保证类型正确,或者做好异常处理。
因为没有类型的约束,lua 语言非常灵活,可以编写一些复杂的逻辑。
除了动态语言共同的优势以外,lua 自身有两大优势:
- 尾调用,使得程序员可以放心的写递归算法,不必担心 StackOverflow
- 标准化的 c api,使得 lua 语言可以与 c 语言互相调用,并且所写的 c 代码是跨平台的,不会因 lua 解释器不同而不同
一、类型系统分为“值类型”和“引用(对象)类型”
值类型有 5 种:
nil
类型,值也是nil
boolean
类型,值为true
、false
number
类型,默认由double
类型实现。m^n
,求m
的n
次方string
类型,'\0'
字符不会截断字符串,可以用'
、"
、[[]]
来构造字符串。
#str
,求 str 的字节数。str=str..str
,连接两个字符串lightuserdata
类型,对应 C 语言的指针值,是值不是对象
引用类型有 4 种:
function
类型,用function(参数列) end
来构造这种对象table
类型,用{}
来构造这种对象thread
类型,不是线程,是协程,不具有并发功能,只能用来分隔不相干的逻辑。
每个thread
有自己的call stack
。由程序员调度所要执行的thread
,其它thread
会暂停userdata
类型,Cocos2dx 里的 c++类的实例,在 lua 里就是这种类型。
对比lightuserdata
,一个是指针本身(32 位或 64 位无符号整数),一个是指针指向的对象
二、function 相关知识
- 传参和传返回值的时候,根据上一讲分的两大类,来传值或传引用。
- 除了传参和传返回值,
function
还会自动捕获外部的变量,术语叫upvalue
,意思是上文中的变量。
upvalue
一律传引用,也就是说,在函数内修改upvalue
,那么函数外也能看到这一改变。
利用自动捕获,function
可以当做容器用,要访问容器中的变量,就必须调用该函数,以达到封装的目的
lua 中很少做封装,常做的只有继承、多态。 - 尾调用。函数内出现
return func(...)
,那么调用栈会先 pop 当前函数,然后把 func 压入调用栈。
这一机制的好处是,可以无限制使用递归算法,而不会发生 stack overflow。
坏处是,调用栈不完整,在栈上看到一个函数,难以推断是哪个函数调用了这一函数,
因为上一个函数可能已经出栈了。写或不写 return,可以控制是否使用尾调用
三、table 相关知识
table
是哈希表,无序容器。当键是从 1 开始的连续整数时,可以当做数组来用(有序)- 与函数自动捕获引用不同,table 可以存值进去
- 增、改:
t[k]=v
删:t[k]=nil
查:if t[k]~= nil then ...
无序遍历:for k,v in pairs(t) do ...
有序遍历:for i,v in ipairs(t) do ...
setmetatable(t,mt)
用来给表 t 设置一个元表 mt,元表中存放的主要是元方法,
比如__add
,__sub
,__mul
,__div
,__len
,__concat
,__gc
。
前面几个相当于“运算符重载”,使 t 可以支持算术运算。gc 是垃圾回收的意思,相当于 t 的析构函数。
可以看出,mt 控制着 t 的行为
四、类与类的实例
- 二者都是
table
,前者装有成员函数(共性),后者装有成员变量(个性) - 语法糖
instance:method(argument)
原型为instance['method'](instance,argument)
先在实例表里查找成员函数,通常表里只有成员变量没有成员函数,这种情况下会触发__index
元方法,
元方法去类表里继续查找,若依然找不到,再触发类表的__index
元方法,一路找下去。
找到之后,就把实例表、实参传入这个函数,于是函数内就能访问到成员变量了 - 语法糖
instance.method(argument)
原型为instance['method'](argument)
这相当于其它语言中调用static
成员函数,没有成员变量传入函数。
或者class:method(argument)
类表会被传入函数,
static 成员变量应该存放在类表里,并且可以被 static 成员函数访问 - 上面的 class 指代一个类表,在 cocos 中,class 是一个全局函数,由 cocos 引擎提供。
这个函数的作用是返回一个新的类表,并为其设置好:元方法__index
(去父类中查找)、
static 成员函数create
(创建实例对象)、static 成员函数new
(创建实例对象)、
一般成员函数ctor
(在实例对象中放入初始内容)。
实例对象可能是table
或userdata
,而类一定是table
。
Cocos 引擎创建userdata
的时候,总是同时创建一个table
,并将二者绑在一起,
所以可以对userdata
使用table
语法 - 上面说的语法糖,不止用于函数调用,也用于函数定义:
local class = {} function class:ctor(sth) self.field = sth end --等价于: local class = {} function class.ctor(self, sth) self.field = sth end
其它
- lua 中任意类型的值都可以代表真假。只有
nil
和false
两个值代表假,其它值都代表真。 and
和or
运算符,不会把它们所连接的表达式转成boolean
型,这一点跟其他语言不同。
a and b or c
与其它语言的a ? b : c
功能类似,具体是:若 a 和 b 为真,则返回 b;
若 a 为假,则返回 c。若 a 为真,b 为假,则返回 c,这与?:
不同。- 参考手册 https://www.lua.org/manual/5.1/
- https://learnxinyminutes.com/docs/zh-cn/lua-cn/