【Lua 入门基础篇(七)】表&模块与包

简介: 笔记

一、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.png

(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 库了。


相关文章
浅谈基于openresty(nginx+lua)开发轻量级,按流量控制的灰度模块(下)
浅谈基于openresty(nginx+lua)开发轻量级,按流量控制的灰度模块
143 0
|
NoSQL 安全 Java
Redis从入门到精通之Lua 脚本
Lua 是一种轻量级的脚本语言,被广泛应用于游戏开发、嵌入式系统、Web 开发、科学计算等领域。Redis 内置了 Lua 解释器,使得用户可以通过编写 Lua 脚本来扩展 Redis 的功能。在 Redis 中,可以使用 EVAL 和 EVALSHA 命令执行 Lua 脚本。
766 7
Redis从入门到精通之Lua 脚本
|
负载均衡 应用服务中间件 测试技术
浅谈基于openresty(nginx+lua)开发轻量级,按流量控制的灰度模块(上)
浅谈基于openresty(nginx+lua)开发轻量级,按流量控制的灰度模块
435 0