Lua
语言支持具有层次结构的模块名,通过点来分割名称中的层次。例如,一个名为 mod.sub
的模块是模块 mod
的一个子模块。一个包是一颗由模块组成的完整的树,它是 Lua
语言中用于发行程序的单位。
当加载一个名为mod.sub的模块时,函数 require
依次使用原始的模块名 "mod.sub"
作为键来查询表 package.loaded
和表 package.preload
。这里,模块名中的点像模块名中的其他字符一样,没有特殊含义。
然而,当搜索一个定义子模块的文件时,函数 require
会将点转换为另一个字符,通常就是操作系统的目录分隔符(例如, POSIX
操作系统的 /
或Windows操作系统的 \
)。转换后,函数 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
语言使用的目录分隔符是编译时配置的,可以是任意的字符串。例如,没有目录层次的系统可以使用下划线作为"目录分隔符",因此调用 require "a.b"
会搜索文件 a_b.lua
。
提示
Lua
并不知道目录的存在。
C
语言中的名称不能包含点,因此一个用 C
语言编写的子模块 a.b
无法导出函数 luaopen_a.b
。这时,函数 require
在加载 C
语言编写的子模块时还有另一个搜索器。当该函数找不到子模块对应的 Lua
文件或 C
文件时,它会再次搜索 C
文件所在的路径,不过这次将搜索包的名称。例如,如果一个程序要加载子模块 a.b.c
,搜索器或搜索文件 a
。如果找到了 C
标准库 a
,那么函数 require
就会在该库中搜索对应的加载函数 luaopen_a_b_c
。这中机制允许一个发行包将几个子模块组织为一个 C
标准库,每个子模块有各自的加载函数。
从 Lua
语言的视角看,同一个包中的子模块没有显式的关联。加载一个模块并不自动加载它的任何子模块。同样,加载子模块也不会自动加载其父模块。当然,只要包的实现者愿意,也可以创造这种关联。例如,一个特定的模块可能一开始就显式地加载它的一个或全部子模块。