Lua支持具有层级性的模块名,可以用一个点来分隔名称中的层级。
比如,一个mod.sub模块,它就是mod的子模块。一个包(package)就是一个完整的模块树。
当你require "mod.sub"时,require首先会用"mod.sub"作为key,去询问package.loaded 然后找package.preload 表。这时模块中的点" . "没有任何特殊意义。
然而当搜索一个定义子模块的文件时,require会将点转换为另一个字符——“系统目录分隔符”。
转换之后,require就像搜索其他名称一样来搜索这个名称。例如,假设路径为:
./?.lua ; /usr/local/lua/?.lua;/usr/local/lua/?/init.lua
调用require "a.b" 后:
./a/b.lua /usr/local/lua/a/b.lua /usr/local/lua/a/b/init.lua
通过这样的加载策略,就可以将一个包中的所有模块组织到一个目录中。
例如,一个包中有模块p、p.a和p.b,那么它们对应的文件名就分别为p/init.lua ,p/a.lua和p/b.lua,它们都是目录p下的文件。
Lua使用的目录分隔符是编译时配置的,可以是任意的字符串。
由于C函数名中不能包含点,因此一个用C编写的子模块a.b无法导出函数luaopen_a.b。
require会将点转换为下划线。例如一个名为a.b的C程序库就应将其初始化函数命名为luaopen_a_b。
在此可以巧用连字符,来实现一些特殊的效果。例如,有一个C程序库a,现在想将它作为mod的一个子模块。
那么就可以将文件名改为mod/v-a ,当require “mod.v-a”时,require就会找到改名后的文件mod/v-a及其中的luaopen_a函数(适应于Lua5.2版本)。
require在加载C子模块时还有一些选项。当它无法找到对应的Lua文件或C程序库时,它会再次搜索C路径,不过这次将以包的名称来查找。
例如一个程序require子模块a.b.c,无法找到文件a/b/c时,再次搜索就会找到文件a。
如果找到了c程序库a,require就查看该程序库中是否有luaopen_a_b_c函数。
这项功能使得一个发行包可以将几个子模块组织到一个单一C程序库中,并且具有各自的open函数。
从Lua的观点看,同一个包中的子模块除了它们的环境table是嵌套的之外,它们之间并没有显式的关联性。
require模块a并不会自动地加载它的任何子模块。同样,require子模块a.b也并不会自动地加载a。
当然,如果包的实现者愿意,他完全可以实现这种关联。
例如:模块a的一个子模块在加载时会显式地加载a。
以上内容来自:《Lua程序设计第二版》和《Programming in Lua third edition 》