模块就是一个程序库,而包是一系列模块。Lua中可以通过require来加载模块,然后得到一个全局变量表示一个table。Lua将其所有的全局变量保存在一个被称为“环境”的常规table中。本文首先介绍环境的一些实用技术,然后介绍如何引用模块及编写模块的基本方法。
1. 环境
Lua将环境table保存在一个全局变量_G中,可以对其访问和设置。有时我们想操作一个全局变量,而它的名称却存储在另一个变量中,或者需要通过运行时的计算才能得到,可以通过value = _G[varname]来获得动态名字的全局变量。
关于“环境”的一大问题是它是全局的,任何对它的修改都会影响程序的所有部分。Lua 5允许每个函数拥有一个子集的环境来查找全局变量,可以通过setfenv来改变一个函数的环境,第一个参数若是1则表示当前函数,2则表示调用当前函数的函数(依次类推),第二个参数是一个新的环境table。
a = 1 setfenv(1, {}) print(a) -- 会报错,print是一个nil。这是因为一旦改变环境,所有的全局访问都会使用新的table
为了避免上述问题,可以使用setfenv(1, {_G = _G})将原来的环境保存起来,然后用_G.print来引用。另一种组装新环境的方法是使用继承,下面的代码新环境从源环境中继承了print和a,任何赋值都发生在新的table中。
a = 1 local newgt = {} setmetatable(newgt, {__index = _G}) setfenv(1, newgt) print(a)
2. 模块与包
2.1 调用模块
要调用模块mod中的foo方法,可以用require函数来加载,如:
require "mod" mod.foo() -- 或者 local m = require "mod" m.foo()
require函数的行为: (关于require使用的路径查找策略不赘述)
在package.loaded这个table中检查模块是否已加载
=> 已加载,就返回相应的值(可见一个模块只会加载一次)
=> 未加载,就试着在package.preload中查询传入的模块名
===> 找到一个函数,就以该函数作为模块的加载器
===> 找不到,则尝试从Lua文件或C程序库中加载模块
=====> 找到Lua文件,通过loadfile来加载文件
=====> 找到C程序库,通过loadlib来加载文件
2.2 使用环境
下面的代码说明了如何用环境来创建一个复数(complex)模块:
-- 模块设置 local modname = "complex" local M = {} _G[modname] = M package.loaded[modname] = M -- 声明模块从外界所需的所有东西 local _G = _G -- 保留旧环境的引用,使用时需要像_G.print这样用 local io = io -- 运行这句之后环境就变了 setfenv(1, M) function new(r, i) return {r=r, i=i} end function add(c1, c2) return new(c1.r + c2.r, c1.i + c2.i) end
这样声明函数add时,就成为了complex.add,调用同一模块的其他函数也不需要加前缀。
2.3 module函数
Lua 5.1提供了一个新函数module,囊括了上面一系列定义环境的功能。在开始编写一个模块时,可以直接用module("modname", package.seeall)来取代前面的设置代码。在一个模块文件开头有这句调用后,后续所有代码都不需要限定模块名和外部名字,同样也不需要返回模块table了。
2.4 子模块与包
Lua支持具有层级的模块名,用一个点来分隔名称中的层级。例如一个模块名为mod.sub,就是mod的一个子模块。一个包(package)就是一个完整的模块树,它是Lua中发型的单位。注意,当搜索一个子模块文件时,require会把点号当做目录分隔符来搜索,也就是说调用require "a.b"会尝试打开./a/b.lua,/usr/local/lua/a/b.lua,/usr/local/lua/a/b/init.lua。通过这种加载策略,可以将包的所有模块组织到一个目录中。
2.5 以自定义方式加载 lua 模块
从 Lua 5.1 以后,Lua 有了标准的模块管理库。所以所有的模块加载都是通过 require 来完成。 require 的设计是颇具扩展性的,它会从若干个定义好的 loader 中逐个尝试加载新的模块。系统库中提供了四个 loader ,分别实现已加载模块,Lua 模块,和 C 扩展模块(用了两个 loader 来实现 C 扩展模块的加载)。这些 loader 以 CFunction 的形式放在 require 的环境中的一个 table 里。
如果我们想改变 lua 模块的加载形式,只需要替换或增加一个新的 loader 就可以了。
要做的只需要模仿 loadlib.c 中的 loader_Lua 函数做一个自己的实现,比如在我们的项目中,就允许从自定义格式数据包中,加载一个被加密过的 Lua 代码文件。然后写几行 C 代码,获得 require 的环境(使用 lua_getfenv ),然后取出其中 "loaders" 这个 table ,把新的自定义 loader 插入到 index 2 的地方。
具体的代码就不详述了,仔细阅读一下 ll_require 的实现(在 loadlib.c 中)就很容易明白。我们的整个工作从分析到实现没有超过两个小时,这真是得益于 Lua 良好的设计啊 :D 甚至如果你想从一个网络连接的数据流中加载 Lua 模块,或是通过 http/ftp 协议下载,也是行的通的吧。
Lua,全局环境,包,模块
RTX 5090要首发 性能要翻倍!三星展示GDDR7显存
三星在GTC上展示了专为下一代游戏GPU设计的GDDR7内存。
首次推出的GDDR7内存模块密度为16GB,每个模块容量为2GB。其速度预设为32 Gbps(PAM3),但也可以降至28 Gbps,以提高产量和初始阶段的整体性能和成本效益。
据三星表示,GDDR7内存的能效将提高20%,同时工作电压仅为1.1V,低于标准的1.2V。通过采用更新的封装材料和优化的电路设计,使得在高速运行时的发热量降低,GDDR7的热阻比GDDR6降低了70%。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]