Lua笔记
最近使用Lua过程中的一些笔记记录,零零散散的,主要有以下这么些内容:
版本兼容性处理
常接触到的Lua版本有5.1、5.2和5.3,在Lua中可以通过_VERSION
获取版本:
1 | if _VERSION == "Lua 5.3" then |
Lua5.1、5.2和5.3的一些常会遇到的API差异:
5.1->5.2
loadstring
改为load
setfenv/getfenv
改为_ENV
5.2->5.3
unpack
改为table.unpack
一种较为简单的兼容API差异的方法是增加类似这样的代码:
1 | load = load or loadstring |
pack和unpack
在Lua中,table和逗号分隔的多个值的互相转化,即将多个值打包成table或者是把table解包成多个值。以下是几个具体的使用场景:
变长参数的函数
将多个值转为table,使用{...}
:
1 | local function (...) |
将会输出:
1 | got 5 arguments |
另一种取值(遍历)的方法是使用select
,当select
的参数为"#"
时,返回这一组参数的长度,当参数为整数时,表示截取此整数位置及其后的所有元素:
1 | local function (...) |
将会输出:
1 | got 6 arguments |
将table值赋给多个变量
1 | local unpack = unpack or table.unpack |
将会输出:
1 | a = 3 |
处理有多个返回值的函数
只有放在最后的时候才能接收到所有的返回值
1 | local function multiRet() |
将会输出:
1 | a = ret1 b = ret2 c = nil |
迭代器
在Lua中,习惯了用ipairs
和pairs
来做遍历,其实它们也只是比较特殊的函数而已:
1 | function ipairs(t) end |
传入参数是一个表,该函数返回三个值,依次是一个迭代函数f
、表t
和0
,因此可以使用:
1 | for i,v in ipairs(t) do |
这种格式来迭代(1,t[1]), (2,t[2]) ...
这样成对的值,直到没有更多的整数索引。
1 | function pairs(t) end |
传入参数是一个表,该函数返回三个值,依次是函数next
、表t
和nil
,因此可以使用:
1 | for k,v in pairs(t) do |
这种格式来迭代表中所有的键值对。
函数next
:
1 | function next(table, index) end |
可用next
遍历整个表,第一个参数是表,第二个参数是表内的一个索引。next
会返回表的下一个索引和关联的值。当第二个参数是nil时,next
会返回一个初始索引和其关联的值。当第二个参数是表的最后一个索引时,或者当表是空表而第二个参数是nil时,next
会返回nil。如果第二个参数空缺,则认为是nil,此时你可以用next(t)
来判断一个表是否为空表。表内索引值的迭代顺序是不确定的,即便是数字型索引。如果在使用next
遍历过程中给遍历中的表增加新的值,将会出现为定义行为。但是你可以修改甚至清除当前已有的值。
使用next
方法,则pairs
函数可理解为:
1 | function pair(t) |
raw方法
使用rawget
和 rawset
避免触发__index
和__newindex
方法:
1 | local t = {a = "a"; b = "b"; c = "c"} |
将会输出:
1 | c |
除此之外还有rawequal
,避免触发__eq
方法:
1 | local eq = function(t1,t2) |
将会输出:
1 | t1 == t2 -> true |
package相关
查找路径
当require
某个脚本被告知module not found
或者不确定脚本路径从哪一级写起时,可以使用以下方法显示当前的查找路径。
1 | print(package.path) |
package.path
中记录了所有的包含路径。如果要增加某个路径,只需像拼接字符串那样接在后边即可:
1 | package.path = package.path .. ";/SomeFolder/?.lua" |
模块重新加载
对于已经加载的Lua模块,如果再次require
会直接得到第一次加载时的内容。加载过的模块都可以在package.loaded
和package.preloaded
中找到。可以手动将其置为nil,然后再重新require
。使用这种方法,可以在运行时不重启程序,修改Lua脚本并应用修改内容,用来调试Lua代码非常方便。
文件夹m下有文件mod1.lua 内容如下:
1 | func = function() |
测试代码,可以在命令行测试:
1 | require ("m.mod1") |
这种操作只会影响到下次require
时执行的动作,但不会影响到已经加载到内存中的内容。
环境
默认情况下,全局变量都保存在一个名为_G
的表里,也可以通过遍历_G
获取全部的全局变量/函数,如:
1 | t = {"a","b","c"} |
setfenv和getfenv
这个_G
就是一个默认的函数环境,有时候我们可能会有一些需求要使用其它的环境而非默认的_G
,在5.1版本的lua中我们可以使用setfenv
和getfenv
来设置和获取环境。
setfenv
接收两个参数,第一个参数为一个函数或者一个数字,第二个参数为设置的目标环境表。当第一个参数为函数时,表示设置该函数的环境,若第一个参数为数字(1、2、3…),1表示当前函数,2表示更外一层即调用当前函数的函数,以此类推。
1 | local newEnv = {} |
_ENV
5.2及更高版本的lua废弃了setfenv
和getfenv
,取而代之使用_ENV
来设置环境,如:
1 | local newEnv = {} |
print
是新的环境中的函数。
沙盒环境
制作沙盒环境,只能访问到希望访问到的函数,并且对全局变量的修改也都是在临时的新环境中进行,上边的例子就是一种应用,将希望在新环境中使用的函数(全局的print
),使用upvalue
的形式(prt
)引用到新的环境中newEnv.print
。
如果在新环境中使用_G
的函数,另一种方法是:
1 |