模块
- 一些代码,通过require加载后,返回到一个table中
- 将加载的内容到package.loaded中
标准库都是模块,Lua会进行提前加载
- 如:Lua会默认加载math = require(“math”)
--在一个没有使用require的程序中输出package.loaded的table
for k, v in pairs(package.loaded) do
print(k, v)
end
--[[
输出:
io table: 0000000000e68df0
debug table: 0000000000e69d70
package table: 0000000000e679e0
utf8 table: 0000000000e699f0
_G table: 0000000000e618f0
coroutine table: 0000000000e68ba0
string table: 0000000000e69c30
table table: 0000000000e68b60
math table: 0000000000e69c70
os table: 0000000000e69bf0
这些表在Lua中的模块都属于标准库
--]]
- 如:Lua会默认加载math = require(“math”)
Lua中模块是第一类值,可以像操作普通表一样操作模块
- 注:前提是模块必须有返回值,否则调用模块返回的是true
可以是定义了一些变量的代码
require(modename):载入名为modname的模块
- modename:要载入的模块名
- 返回值:取决于加载函数
- 加载函数有返回值:require函数返回该值,并保存到package.loaded中
- 加载函数无返回值:require函数返回true,并保存到package.loaded中
- 返回true是为了防止模块重复加载
- require执行步骤:
- 在package.loaded中检查模块是否已被加载
- 已经被加载:返回package.loaded中相应的值(模块被加载后,同意模块调用都返回同一值)
- 模块未加载:搜索指定模块的Lua文件(搜索路径由package.path指定)
- 找到文件:使用loadfile加载Lua文件,并添加到package.loaded中
- Lua文件未找到:搜索C标准库(搜索路径由package.cpath指定)
- 找到C标准库:使用package.loadlib进行加载
- 在package.loaded中检查模块是否已被加载
注:模块在只能加载一次
--[[ 强制加载同一模块多次 --]] package.loaded[.modname] = nil --先将原本的删除
模块的重命名
如果模块名中含有连字符,会被视为使用连字符之前的内容来创建luaopen_*函数
require使用的路径是一组模板
- 模板:每一个模板都是一个包含可选问号的路径名,会使用模块名替换问好。
- 替换后检查文件是否存在
- 不存在,尝试下一个模板
- 如:路径为:?;?.lua;c:\windows\?.lua
- 使用该路径调用sql模块时,将会打开如下Lua文件
- sql
- sql.lua
- c:\windows\sql.lua
- 使用该路径调用sql模块时,将会打开如下Lua文件
- 模板:每一个模板都是一个包含可选问号的路径名,会使用模块名替换问好。
require用于搜索Lua文件的路径是package.path的当前值(C标准库的搜索路径为package.cpath)
package.searchers(modname,path):package.searchers函数中实现了搜索库中的所有规则,该函数使用规则,将modname带入path中进行检查。
- modname:模块名
- path:要检查的搜索库规则
- 返回值:
- 存在文件,则返回文件名
- 不存在文件,返回nil及文件的错误信息 ```lua local path1 = “D:\lua-5.4.3-setup\lua\bin\lua\?.lua;.\?\init.lua” local path2 = “D:\lua-5.4.3-setup\lua\bin\lua\?.lua;.\?\init.lua;?.lua”
print(“==cheack path1==”) print(package.searchpath(“test2”, path1)) print(“==check path2==”) print(package.searchpath(“test2”, path2))
—[[ 输出: ==cheack path1== nil no file ‘D:\lua-5.4.3-setup\luin\lua\test2.lua’ no file ‘.\test2\init.lua’ ==check path2== test2.lua —]]
<a name="B0Fml"></a>
#### 搜索器
- **table package.searchers**中**包含**了**require**所使用的**4个搜索器函数**。
- 在**require**传入**模块名**时,**依次调用搜索器**,**直到找到**指定模块(找不到则抛出异常)
- **package.searchers中**的函数:
1. 第**一**个查找器:在**package.preload** table**中查找**
1. 第**二**个查找器:**查找Lua库的加载库**,在**package.path中查找**。过程与package.searchpath函数一致
1. 第**三**个查找器:**查找C库的加载库**,在**package.cpath中查找**。过程与package.searchpath函数一致
1. 第**四**个查找器:**一体化加载器**,从C路径中查找指定模块的根名字
1. 自己添加的搜索器函数
```lua
package.searchers[5] = function() --在搜索器table package.searchers中添加第五个搜索器
print("this is searchers 5")
end
mod = require("null") --require调用null模块时,会依次使用package.searchers中的搜索器
mod.show()
--[[
输出:(该情况是null模块不存在的情况下)
this is searchers 5
报错信息
..
--]]
编写模块的基本方法
- 最简单的方法,创建一个表并将所有需要导出的函数放入其中,最后返回这个表。 ```lua local M = {} —模块
local function new(r, i) —使用local设定为私有函数 return {r = r, i = i} end
local function inv(c) —使用local设定为私有函数 local n = c.r ^ 2 + c.i ^ 2 return new(c.r / n, -c.i / n) end
M.new = new —将new加入到模块中
M.i = new(0, 1)
function M.add(c1, c2) return new(c1.r + c2.r, c1.i + c2.i) end
function M.sub(c1, c2) return new(c1.r - c2.r, c1.i - c2.i) end
function M.mul(c1, c2) return new(c1.r c2.r, c1.i c2.i) end
function M.div(c1, c2) return M.mul(c1, inv(c2)) end
function M.tostring(c) return string.format(“%g,%g”, c.r, c.i) end
return M
—[[
return的替换方式1:
package.loaded[…] = M
return的替换方式2: return{ new =new i=i add=add … } —其中函数及变量全部为模块中的全局变量 —]] ```
- 注意:如何将new和inv声明为局部变量而使其成为私有函数
- 其中return M代码段的替换
- 可以使用package.loaded[…] = M代替(但是使用return更加清晰)
- 因为require会将其模块名称作为参数传给加载函数,…就是模块名
- 还可以将所有函数定义为局部变量,最后返回一个构造的表
- 优点:无需增加前缀M.。
- 缺点:导出的表在模块最后,比较冗余
- 可以使用package.loaded[…] = M代替(但是使用return更加清晰)
子模块和包
子模块
子模块:Lua利用层次结构的模块名,使用点进行分割。
包:由模块组成的完整的树,发行程序的单位
- 即将所有模块放在一个文件夹下