Lua unpack和require探究
table.unpack
-
先思考一段代码:
function printParam(a, b, c, d, e, f) print(a, b, c, d, e, f) end function Test() local a = {nil, 2, nil, 4, nil, 5} printParam(unpack(a)) end Test()
结果(以下结果均在lua5.1测试):
nil 2 nil 4 nil 5
好像不出所料,打印的就是我们table表里的所有参数,都满足我们的预期
-
在变化一下呢:
function printParam(a, b, c, d, e, f) print(a, b, c, d, e, f) end function Test() local a = {nil, 2, nil, 4, nil, nil} printParam(unpack(a)) end Test()
结果:
nil nil nil nil nil nil
有没有出乎意料呢?为什么打印的结果全是nil呢?
翻阅一下Lua官方文档
可以看到unpack其实是遍历list表,从下标0遍历到#table的长度。我们再看看#table的定义:
概括一下,一个table表里面,会被nil划分成多个边界,当table存在多个边界的时候,#table可以返回它的任何边界的值(取决于表的内部表示的细节,而这又取决于表的填充方式及其非数字键的内存地址)。
- 这时候结合去理解一下用例二
我们用例二的table表是{nil, 2, nil, 4, nil, nil},这个时候就有多个边界为(0,2,4),而返回的边界这时候是0。注意:你可能在这个过程中会发现一些规律,但是Lua告诉我们这不是一个合理的规律,我们要做的就是避免table表中的nil值。
创建我们自己的unpack去规避问题
table.pack2 = function(...)
return { n = select('#', ...), ... }
end
table.unpack2 = function(t)
return table.unpack(t, 1, t.n)
end
通过存储n的长度,避免#table的读取错误,从而规避unpack的问题
Lua Require
先来了解一下require的处理流程:
-
require(modelname)
require(在lua中它是ll_require函数)的查找顺序如下:-
首先在package.loaded查找modelname,如果该模块已经存在,就直接返回它的值
-
在package.preload查找modelname, 如果preload存在,那么就把它作为loader,调用loader(L)
-
根据package.path的模式查找lua库modelname,这个库是通过module函数定义的,对于顶层的lua库,文件名和库名是一 样的而且不需要调用显式地在lua文件中调用module函数(在ll_require函数中可以看到处理方式),也就是说lua会根据lua文件直接完 成一个loader的初始化过程。
-
根据package.cpath查找c库,这个库是符合lua的一些规范的(export具有一定特征的函数接口),lua先已动态的方式加载该c库,然后在库中查找并调用相应名字的接口,例如:luaopen_hello_world
-
已第一个"."为分割,将模块名划分为:(main, sub)的形式,根据package.cpath查找main,如果存在,就加载该库并查询相应的接口:luaopen_main_sub,例如:先查找 hello库,并查询luaopen_hello_world接口
-
得到loder后,用modname作为唯一的参数调用该loader函数。当然参数是通过lua的栈传递的,所以loader的原型必须符合lua的规范:int LUA_FUNC(lua_State *L)
-
最后注意:ll_require会将这个loader的返回值符给package.loaded[modelname],如果loader不返回值同时 package.loaded[modelname]不存在时, ll_require就会把package.loaded[modelname]设为true。最后ll_reuqire把package.loaded [modelname]返回给调用者。uqire把package.loaded [modelname]返回给调用者。
-
-
了解了加载的流程,这时候当我们require过了一个模块后,如果这个模块被修改了(运行时等情况),我们是不能通过调用require来使得这个模块重新加载的。有一个通用的方法
package.loaded[file] = nil package.preload[file] = nil
保险起见我们一般都是把preload的值也置空,避免出现遗漏的情况。