基础特性
Last updated
Last updated
详实准确,但是英文的。
简易实用,可以快速浏览。
nil
a=nil 等效为删除a。未赋值的区域是nil的海洋
boolean
只有 nil 和 false 会被判断为假
number
lua5.3引入了整数,其类型也是number,但支持位操作。例如 2 | 3 = 3; 1 & 2=0;
string
thread
lua的 thread
其实是协程。其和真正的线程差不多,拥有独立的栈、局部变量和指针,可以和其他thread共享全局变量。但是,真正的线程可以同时多个运行,而协程任意时刻只能运行一个。
userdata
table
lua唯一的数据结构。
默认为全局,需要显式声明为 local
才为局部变量。
访问局部变量更快。如有必要,可将全局变量的引用保存到局部,加快访问速度,也可同步更改全局变量的值。
可作为参数传递
支持匿名函数
默认为 public 函数,用local声明则为该模块的私有函数
【语法糖】func({1,2,3})
可简写为func{1,2,3}
【语法糖】
定义时。function t:func(p1,p2)
等效为 function t.func(self,p1,p2)
调用时。t:func(1,2)
等效为 t.func(t,1,2)
包(package)就是一系列的模块(module)
引用一个叫 var 的自由名字(指在任何层级都未被声明的名字)在句法上都被翻译为 _ENV.var
。 此外,每个被编译的 Lua 代码块都会有一个外部的局部变量叫 _ENV。
_ENV
是一个table,被称为环境
全局环境
叫做_G
,被保存在c注册表的一个特别索引下。而且,_G._G == _G。
所有的标准库都被加载入全局环境
若干table可以共用一个元表,用于共享一些自定义的行为,如两个表相加。
一个表的元表可以设定为自己
__index
元方法。当我们对一个表进行键值访问t.anonexsitkey
,内部触发的逻辑顺序是:
在表t中找anonexsitkey这个键,找到则返回对应值,找不到执行下一步
找t的元表并判断元表有没有__index,找不到返回nil,有则执行下一步
__index若是函数则调用。其若是表就在这个表中找键值(这其实也可以理解为一个语法糖,因为用的太频繁)。
注:不想触发__index则用rawget(t,key)
__newindex
元方法。当我们对一个表的键赋值t.anonexsitkey = myValue
,内部触发的逻辑顺序是:
在表t中找anonexsitkey这个键,找到则替换成myValue,找不到执行下一步
找t的元表并判断元表有没有__newindex,找不到则替换成myValue,有则执行下一步
__newindex若是函数则调用(myValue就不执行赋值),若是表则将myValue更新到这个表里而不是t中(有点像直接改变基类的成员)。
注:不想触发__newindex则用rawset(t,key,value)
assert( l1, msgStr)
l1为false则打印msgStr
error(msgStr, level)
level=0,1,2
pcall(func, param1, param2)
保护模式运行func,忽略func内部的错误处理,返回true或false
xpcall(func, errhandle, param1,param2)
和pcall类似,但自定义错误处理函数。结合:
debug.traceback()
lua没有class,而是类似prototype-based. 所谓prototype, 其实就是一个普通的实体,但只有一个作用:提供公共的行为。所以要切换一下思维:lua中的“类”不是一个抽象的概念,而是一个个活生生的原型实体。
下面的写法让b成为a的prototype(也可以称作b是一个class). a的metatable在这里是匿名的。
但这样每次以b为原型创建对象时都会新建一个metatable,挺浪费的。优化的写法是:
接着上面,如果b这张表里定义了一个键对应一个函数,比如 b:func
则现在a也可以调用
如果a也想作为一个类,并且继承b呢?下面将a打造成b的子类,改变func,创造出以a为原型的c
上述会输出defined by a
而不是会输出defined by b
,妙就妙在b:new中用了self。如果把self直接改写为b,那么由于b就是个实体,那么,b.new(a,nil)就还是会把c的metatable变成b,b.__index=b。
lua和c存在两种调用关系
lua作为主程序,调用c编译出的库(例子,算法加速)
c作为主程序,调用lua编译出的库(例子,lua翻译器本身)
lua和c通信通过一个虚拟栈,来解决两种语言的差异:
GC。虚拟栈由lua维护,所以知道哪些正被C使用,故不清除。
虚拟栈有两种索引顺序:
1.栈底(1)->栈顶(n)
2.栈顶(-1)->栈底(-n)
C可以访问和修改栈任意位置的值,lua只能按照LIFO原则访问修改栈顶。
压栈
lua->栈的lua_pushunumber
c->栈,userdata
?稍后讨论
一次压栈太多要防止超过容量。lua_checkstack
取值
lua从栈上取值要判断类型,利用lua_isnumber
之类的
c从栈上取值,利用lua_tonumber
之类的,将返回double
如果取的是table的某个键值,这里有一个特殊处理(假设lua中有一个bgcolor = {r=1, g=0, b = 0}):
例子1
例子2
c调用lua的一个函数myluafunc
open lua state L (lua_open
) 注:创建了一个全新的独立的lua环境
load lib into L (luaL_openlibs
)
compile commandStr in buff , and push compiled code to L's stack - (luaL_loadbuffer
)
pop code from L's stack and execute in protect mode (lua_pcall
)
if lua throws error msg in L, print & pop it (lua_tostring
, lua_pop
)
close lua state L(lua_close
)
lua调用c库,
c库会报错导致程序crash,标准的处理是让c库调用lua_error
c调用lua,
分配栈空间不足也会报错导致crash(除非使用了保护模式即lua_pcall
,其会返回错误码)
就是一个定义了一个table的文件,最后 return 该模块名称。
require "module_name",会先从package.loaded
中找,再用lua加载器从package.path
中找,再用c加载器从package.cpath
中找,再用
lua53去掉了setfenv和getfenv,转而用_ENV和upvalue的概念。
__metatable
元方法。若mt是t的元表,则保护t的元表不被改变,可以 mt.__metatable = 'i am hiden and non-changable'
。这样getmetatable(t)会给出这句话,setmetatable(t,other)会报错。
更多元方法简介见
更多
变量类型。主要是解决了若干种C类型和若干种Lua类型关联的。每种类型只需要实现和虚拟栈中统一类型的转化函数。