一、table表
table 是一种数据结构用来帮助我们创建不同的数据类型(数组、字典等)。
table 使用关联型数组,可以用任意类型的值来作数组的索引,不可是nil。
table 不固定大小。
Lua 也是通过table来解决模块(module)、包(package)和对象(Object)的。
1. 表的构造
构造器是创建和初始化表的表达式。最简单的构造函数是{},用来创建一个空表。
mytable = {} -- 初始化表 mytable[1] = 'lua' -- 指定值 mytable = nil -- 移除引用 -- lua 垃圾回收会释放内存
创建table a并设置元素,将a赋值给b,则a与b都指向同一个内存。如果a设置为nil,则b同样能访问table的元素。如果没有的变量指向这片内存,Lua的垃圾回收机制才会清理这片内存。
2. 表的操作
(1) concat(连接)
a = {'a', 'b', 'c'} print(table.concat(a)) print(table.concat(a, ", ")) print(table.concat(a, " <-> ", 2, 3))
abc a, b, c b <-> c
(2) insert(插入)
a = {'A', 'B'} table.insert(a, 'C') print(a[3]) --> C
在索引为 1 处插入
a = {'A', 'B'} table.insert(a, 1, 'C') for k, v in pairs(a) do print(v) end
运行结果:
C A B
(3) remove(移除)
- 默认删除table最后一位。
a = {'A', 'B', 'C'} table.remove(a) for k, v in pairs(a) do print(v) end --[[ A B --]]
- 指定删除索引为 1 的,后续元素前移。
a = {'A', 'B', 'C'} table.remove(a, 1) for k, v in pairs(a) do print(v) end --[[ B C --]]
(4) sort(排序)
a = {'c', 'd', 'b', 'a'} table.sort(a) for _, v in ipairs(a) do print(v) end
a b c d
(5) maxn(最大值)
table.maxn 在 Lua5.2 之后该方法已经不存在了,我们定义了 table_max 方法来实现。 function table_max(a) local mx = nil for k, v in pairs(a) do if mx == nil then mx = v end if mx < v then mx = v end end return mx end table = {[1] = 1, [3] = 3, [4] = 8, [10] = -1} print(table_max(table)) -- 8
二、模块与包
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。
Lua 的模块是由变量、函数等已知元素组成的 table,因此创建一个模块很简单,就是创建一个 table,然后把需要导出的常量、函数放入其中,最后返回这个 table 就行。以下为创建自定义模块 module.lua,文件代码格式如下:
-- module.lua -- 创建空表 module = {} -- 常量 module.constant = "const" -- 公有函数 function module.func1() io.write("public func") end -- 私有函数 local function func2() print('private func') end function module.func3() func2() end return module
模块的结构就是一个 table 的结构,因此可以像操作调用 table 里的元素那样来操作调用模块里的常量或函数。
上面的 func2 声明为程序块的局部变量,即表示一个私有函数,因此是不能从外部访问模块里的这个私有函数,必须通过模块里的公有函数来调用。
1. require
Lua提供了一个名为require的函数用来加载模块。
require("模块名")
或者
require "模块名"
执行 require 后会返回一个由模块常量或函数组成的 table,并且还会定义一个包含该 table 的全局变量。
require "module" print(module.constant) -- const module.func3() -- private func
起别名:
local m = require "module" print(m.constant) m.func3()
2. 加载机制
对于自定义的模块,模块文件不是放在哪个文件目录都行,函数 require 有它自己的文件路径加载策略,它会尝试从 Lua 文件或 C 程序库中加载模块。
require 用于搜索 Lua 文件的路径是存放在全局变量 package.path 中,当 Lua 启动后,会以环境变量 LUA_PATH 的值来初始这个环境变量。如果没有找到该环境变量,则使用一个编译时定义的默认路径来初始化。
3. C包
Lua和C是很容易结合的,使用 C 为 Lua 写包。
与Lua中写包不同,C包在使用以前必须首先加载并连接,在大多数系统中最容易的实现方式是通过动态连接库机制。
Lua在一个叫 loadlib 的函数内提供了所有的动态连接的功能。这个函数有两个参数:库的绝对路径和初始化函数。
local path = "/usr/local/lua/lib/libluasocket.so" local f = loadlib(path, "luaopen_socket")
loadlib 函数加载指定的库并且连接到 Lua,然而它并不打开库(也就是说没有调用初始化函数),反之他返回初始化函数作为 Lua 的一个函数,这样我们就可以直接在Lua中调用他。
如果加载动态库或者查找初始化函数时出错,loadlib 将返回 nil 和错误信息。我们可以修改前面一段代码,使其检测错误然后调用初始化函数:
local path = "/usr/local/lua/lib/libluasocket.so" -- 或者 path = "C:\\windows\\luasocket.dll",这是 Window 平台下 local f = assert(loadlib(path, "luaopen_socket")) f() -- 真正打开库
一般情况下我们期望二进制的发布库包含一个与前面代码段相似的 stub 文件,安装二进制库的时候可以随便放在某个目录,只需要修改 stub 文件对应二进制库的实际路径即可。
将 stub 文件所在的目录加入到 LUA_PATH,这样设定后就可以使用 require 函数加载 C 库了。