lua学习笔记day12-----环境

本文深入探讨了Lua语言中代码块的运行环境及其对全局变量的影响。通过具体示例讲解了如何通过不同的方法在非全局环境下保留必要的全局变量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这一天的内容包含了手册上的两个大的章节,主要介绍了以下内容:
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表)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值