***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************
本篇文章主要内容:
> lua的环境
> lua的全局变量
> lua的非全局变量
> lua的模块与包
> require
> 编写模块的基本方法
1.环境
你可以 用下面这个语句,看看,全局变量都有什么东西:
for n in pairs(_G) do print(n) end
刚开始,你一定写过
a = 3 print(a)
上面说过,全局变量是个table,在table查询、更新 表时,需要访问元表,据此可以做一个机制:
setmetatable(_G, { __newindex = function(_, k) error("Attempt to write to undeclared variable "..k) end, __index = function(_, k) error("Attempt to read undeclared variable "..k) end }) print(num)
或许,读到这,你就可能去实践了:
a = 1 setfenv(1, {}) print(a)
我们需要记录下来原来的全局变量:
a = 1 setfenv(1, {_G = _G}) _G.print(a) _G.print(_G.a)
a = 1 local newgt = {} setmetatable(newgt, {__index = _G}) setfenv(1, newgt) print(a)
4.lua的模块与包
通常,lua不会设置规则,但是它会提供许多强力的机制来让开发者去设置规则。如果有用户要调用模块中的某个函数,最简单的方法是:
require "modu" modu.foo()
local m = require "modu" m.foo()
require "modu" local f = modu.foo f()
require的行为:
function require (name) -- 先检查模块是否加载 if not pachage.loaded[name] then local loader = findloader(name) if loader == nil then error("unable to load module "..name) end -- 标记 模块已加载 package.loaded[name] = true -- 初始化模块 load res = loader(name) if res ~= nil then package.loaded[name] = res end end return package.loaded[name] end
因此,只要模块加载过,后续的require调用都会返回同一个值,不会再次去加载它。
如果模块没加载,就会去为这个模块找一个加载器(loader),先在package.preload中查询传入的模块名。如果在其中找到函数就以该函数作为模块的加载器。
通过preload table,就有了一种通用的方法来处理各种不同的情况。通常这个table中不会找到有关指定模块的条目,那么require就会尝试从lua文件或C程序库中加载模块。
如果 require找到了一个lua文件,就用loadfile来加载;如果 找到了C程序库,就用loadlib来加载。(但这两种,都仅仅是加
载,并没有运行代码)
在上面的代码中,在调用加载器前,require先将true赋予package.loaded中对应字段,一次将模块标记为加载,这是非常必要的,
因为如果 A需要requireB,而在B中又require到A,就会导致无限循环。
之前有说过,require对同一个库,只加载一次,如果非要多次加载,就可以将package.loaded中的相应模块条目进行删除:
package.laoded["相应模块"] = nil require "相应模块"
在使用LUA_PATH时,lua会将其中所有的子串";;"替换成默认路径。
比如,路径为: mydir/?.lua;; 那么最终的路径就是 mydir/?.lua 并紧随默认路径。
如果没有相符的lua文件,它就会找C程序库。这类搜索会从变量 package.cpath(相对于 package.path)获取路径。而这个变量则是通过环境变量 LUA_CPATH(相对于LUA_PATH) 来初始化。
在lua中创建模块嘴简单的方法是,创建一个table,并将所有需要导出的函数放入其中,最后返回这个table。
下面是实现 复数 相关的操作:
<1> 初始版本
complex = {} function complex.new(r, i) return {r = r, i = i} end complex.i = complex.new(0, 1) function complex.add(c1, c2) return complex.new(c1.r + c2.r, c1.i + c2.i) end function complex.sub(c1, c2) return complex.new(c1.r - c2.r, c1.i - c2.i) end function complex.mul(c1, c2) return complex.new(c1.r*c2.r - c1.i*c2.i, c1.r*c2.i + c1.i*c2.r) end function comlex.inv(c) local n = c.r^2 + c.i^2 return complex.new(c.r/n, -c.i/n) end function complex.div(c1, c2) return complex.mul(c1, inv(c2)) end return complex
但,如果我们要换个模块名,是不是会很崩溃呢?————每个函数名的前缀,调用函数的前缀都要换!!
而且,每次都要return,好麻烦,万一忘了...
所以,做一些改进:
local modname = ... local M = {} _G[modname] = M package.loaded[modname] = M <如前>
<3> 还可以更精进一些,每次都要写个前缀,好烦啊~,之前有讲过 setfenv ,可以派上用场了,
当然,全局的变量还是要继承过来的
local modname = ... local M = {} _G[modname] = M package.loaded[modname] = M setmetatable(M, {__index = _G}) setfenv(1, M)
<4> 这样做,很方便,但是,此时 我们的模块包含了全局变量,这是非常不好的。
所以,要用一些正规的手段,比如将需要用到的函数或模块声明为局部变量,然后关掉外部访问
local modname = ... local M = {} _G[modname] = M package.loaded[modname] = M -- 接下来,将用到的声明为局部变量: local sqrt = math.sqrt local io = io -- 关闭外部访问 setfenv(1, M)
But,在lua5.2中,官方建议大家放弃 module/package机制,废弃了 module、setfenv、getfenv。
对于一个 没有绑定任何声明的变量,会被转换为带_ENV前缀的变量,比如:
val = 3 local v = 5 print(val) print(_ENV.val) print(v) print(_ENV.v) -- result 3 3 5 nil
function fun(val1) val2 = 2 local val3 = 3 print(_ENV.val1) print(_ENV.val2) print(_ENV.val3) for val4=1,1 do print(_ENV.val4) end end fun(1) -- result nil 2 nil nil
在5.1版本中,我们可以为一段代码块(or函数)设置环境 —— 通过函数setfenv
但这样,会导致一段代码访问全局变量的时候使用了setfenv制定的table,而不是_G.
在5.2版本中 setfenv被废弃,我们可以通过在函数定以前覆盖_ENV变量来为函数定义设置一个全新的环境。
***************************************转载请注明出处:http://blog.csdn.net/lttree********************************************