这一天的内容包含了手册上的两个大的章节,主要介绍了以下内容:
1、一个代码块运行时,是有它自己的环境的
2、在代码运行过程种,或多或少需要一些库函数,如果有两个库函数的文件里有两个名字一样的函数,该如何区分。(就像C++中,用命名空间来区分两个文件中函数名称一致的函数)
3、如何制作自定义的package
环境:
首先解释一下这个词,在一个连续运行代码块(chunk)运行的过程中,会把所有需要的变量都放在一个表里,要注意lua是把函数当作第一类值的,所以这个表里也有函数的名字。
如果这张表是_G,那这个chunk就是运行在全局的环境之下,这个chunk可以直接使用_G中的库函数变量,而chunk创建的全局变量也会登记到这个表里。
如果这张表不是_G,而是自己提供的一张表,那就是非全局的环境了,如果有chunk运行在这个环境底下,它只能用自定义的这张表里提供的资源,如果是张空表,就表示这个环境什么都没有,连print都用不了。而在这个chunk下创建的全局变量,是会登记到自定义的那张环境表里去的。
在lua5.1之前使用setfenv函数来改变一个chunk的环境,
而5.1之后,用了一个类似宏定义的东西 _ENV来改变当前环境。
(我看的是5.1的手册,用的是5.2以上版本的lua,测试代码的时候,被搞死了。)
好了,根据上述内容,提供了以下代码,来验证一下_G,环境,全局变量和局部变量
do
for k in pairs(_G) do
print(k)
end
_ENV = {}
for k in pairs(_G) do
print(k)
end
end
----------------------------------------
require
collectgarbage
tonumber
select
_G
pairs
coroutine
tostring
rawlen
getmetatable
error
type
print
setmetatable
io
unpack
test
pcall
dofile
rawget
xpcall
math
loadfile
arg
bit32
_VERSION
rawset
string
next
loadstring
table
rawequal
package
load
os
module
debug
ipairs
assert
lua: Demo.lua:37: attempt to call global 'pairs' (a nil value)
stack traceback:
Demo.lua:37: in main chunk
[C]: in ?
第一个泛型for对_G这张表进行迭代输出所有的值,
然后用_ENV把当前的环境设置成空表,
再次用for循环迭代_G,就报错了,注意看报错的内容,pairs是个空值!
的确不能用了。
非全局的环境
其实这个内容在手册里用的是非全局变量,但是我觉的它讲的内容更注重于非全局的环境。
上面的内容已经描述了lua允许改变一个chunk的环境。代码运行的结果也很直接的表现出了问题,如果设置的环境不恰当,那这个chunk的后续代码是会出问题的,毕竟什么库函数都用不了。
手册中循序渐进的讲了很多,这里我就直接描述最后的结果。
手册中提供了好几种方法,最终的目的都是在非全局环境中保留全局变量,至少保留要用的:
1、把当前环境的表的metatable设置成一个__index = _G的表,继承原来的换进
2、把_G = _G,当作当前环境表的一个域
3、在chunk的开头把一些要用的全局变量声明称局部变量,local print = print
3是最快的,1是最方便的。
用以下代码演示一下:
do
for k in pairs(_G) do
print(k)
end
env = {}
setmetatable(env,{__index = _G})
_ENV = env
for k in pairs(_G) do
print(k)
end
end
这个是第一种,把env设置成当前的环境后。调用pairs,lua在env的表里找不到,就去env的metatable的__index去找,发现__index是个表,然后拿着pairs当作索引去找,就找到这个变量了。print同理。chunk运行的结果就是_G打印了两次,太长了,结果就不放了。
do
for k in pairs(_G) do
print(k)
end
env = {}
env._G = _G
_ENV = env
for k in _G.pairs(_G._G) do
_G.print(k)
end
end
这个是第二种,把原先的_G作为我非全局环境表的一个域,调用库函数的时候,要加一个索引,要不然会报错。这个结果也是打印两次_G表。
do
local print = print
local pairs = pairs
for k in pairs(_G) do
print(k)
end
_ENV = {}
for k in pairs(_G) do
print(k)
end
end
------------------------------
lua: Demo.lua:40: bad argument #1 to 'pairs' (table expected, got nil)
stack traceback:
[C]: in function 'pairs'
Demo.lua:40: in main chunk
[C]: in ?
(目前我是觉得,一个chunk里的局部变量是不受环境表控制的,反正我试了一下,只要是在一个chunk里声明的局部变量,即使环境是一张空表,对局部变量操作都是不会报错的。)
这个是第三种,把要用的pairs和print用局部变量声明,然后直接用。这个方法很神奇。注意看最后的报错,它的意思是,pairs函数有个不对的参数。没有用局部变量声明_G,报错是对的。
最后,来看一下在改变环境表之后,再声明一个全局变量会发生什么。
do
env = {}
setmetatable(env,{__index = _G})
_ENV = env
num = 1
for k in pairs(_G) do
print(k)
end
print("--------------")
for k in pairs(env) do
print(k)
end
end
---------------------
load
tonumber
print
bit32
string
next
collectgarbage
dofile
rawget
loadstring
arg
error
assert
ipairs
select
pairs
rawequal
tostring
package
os
table
coroutine
env
pcall
math
test
_G
debug
io
unpack
require
module
xpcall
loadfile
setmetatable
getmetatable
rawlen
type
_VERSION
rawset
--------------
num
没错,声明的全局变量变成了env的一个域,而在_G中找不到它,这也是非全局环境的应用所在。
最后总结一下:
环境就是一张普通的表,只不过全局变量的表不是由chunk指定的,它是由lua生成的,所以可以包含库函数之类的。
非全局环境最重要的是保证chunk内用到的全局变量需要保存。在非全局环境的环境里声明的全局变量,不会污染真正的全局变量(_G表)。