Chapter 14 The Environment

Lua keeps all its global variables in a regular table, called the global environment.
(To be more precise, Lua keeps its “global” variables in several environments,

but we will ignore this multiplicity for a while.)


Lua stores the environment itself in a global variable_G. (Yes,_G._G is equal to _G.)

For instance, the following code prints the names of all  global variables defined in the global environment:
for k,v in pairs(_G) do

print(k,v)

end


14.1 Global Variables with Dynamic Names

value = loadstring("return " .. varname)();// Luat5.1 is loadstring, for Lua5.2 is load, you can print _G  to see the function


value 的值需要运行时才能确定。。。


loadstring/ load return a function ,  However, this code involves the creation and compilation of a new chunk, which is somewhat expensive


更高效的方法是:

value = _G[varname] --这样value 也是动态的取决月varname


In a similar way, you can assign a value to a global variablewhose name is
computed dynamically, by writing _G[varname]=value.
Beware, however: some
programmers get a little excited with these facilities and end up writing code
like _G["a"]=_G["var1"],  这样的话, a,var 其实 a const string, not a dynamic variable,也不能写成_G.a=_G.var1. the same effect as_G["a"]=_G["var1"], 

which is just a complicated way to write a=var1.


====如果想实现var =_G.a.b.c.e 的效果,就是table of table,  just write var=_G[a.b.c.e] 当然是不行啦,需要做处理。


function getfield (f)
    local v = _G -- start with the table of globals
    for w in string.gmatch(f, "[%w_]+") do
    v = v[w]
    end
return v
end

getfield("a1_.b2.c3.d4"); --前提是_G里面存了 相应的table of table.

====================================================================

这里我们解析[%w_]+  patter, you can refer my other CSDN Block. or check the Lua reference manual.


%w 是字母数字的匹配

_ 就是_
字符拉

[] 关键是[set] , 按组进行匹配, ie:  [%w_] ,意思就是  字母数字加_ 一组返回,当遇到不是字母数字or _,前面找到的就当一组返回,然后继续直到再次遇到字母数字or _, 。。。。。

ie: abc_.defg.[abc_]  , will return 3 set.

abc_

defg

abc_   --[] 不时字母数字_,所以ommit, 而不会返回[abc_]



offical interpertor:

[set]:represents the class which is theunion of allcharacters inset. A range of characters can be specified byseparating the end characters of the range with a '-'.All classes%x described above can also be used as components inset.All other characters inset represent themselves.For example,[%w_] (or[_%w])represents all alphanumeric characters plus the underscore,[0-7] represents the octal digits,and[0-7%l%-] represents the octal digits plusthe lowercase letters plus the '-' character.


the next example collects all pairs key=value from thegiven string into a table:

     t = {}
     s = "from=world, to=Lua"
     for k, v in string.gmatch(s, "(%w+)=(%w+)") do
       t[k] = v
     end


本来gmatch 只是返回以各匹配到的value, 但从上面的例子看到 () 为一组,找到第一个括号的patter string, not  return

just continue to find, 而且他这种写法 expect our source string will flow by a = , then 继续第二个括号的匹配 .

然后返回的是两个value.


   t = {}
     s = "from_=world_, to=Lua_"
     for k, v in string.gmatch(s, "([%w_]+)=([%w_]+)") do --因为除了  字母我们还允许_ ,所以此时就要用到set 了
     t[k] = v
     end


====================================================================


========my implement version

function getfield (f)
    local v = _G -- start with the table of globals
    for w in string.gmatch(f, "[%w_]+") do
     v = v[w]
      end
return v
end

function setfield(s,v)
  local t=_G;
  for w,d in string.gmatch(s,"([%w_]+)(%.?)") do ---%. is %x 的语法 , 因为. 是magic pattern.  
    --%x: (where x is any non-alphanumeric character) represents the character x. This is the standard way to escape the magic characters.
    if d=='.' then
       print(w,d);
        t[w]=t[w] or {};
        t=t[w]; --其实这里会出现一格问题,如果在前面程序的运行过程中 我们不小心放了一相同的变量,那么这里可能会出问题  因为会解析出这个变量赋给t,比如如果是数字,那么下次再来的时候就出问题了.
        print(t);
    else
       print(t,w);
       t[w]=v;
    end
   end   
end
setfield("a1_.b2.c3.d4",5);
print(getfield("a1_.b2.c3.d4"));


================================= 上面的都是些很好的函数值的放到自己的库里面

`14.2 Global-Variable Declarations

Global variables in Lua do not need declarations. Although this is handy for
small programs, in larger programs a simple typo can cause bugs that are
difficult to find.


---学到这里我留意到,以前写的function, declared variable, all put into this _G global table.



setmetatable(_G, {
__newindex = function (_, n)
error("attempt to write to undeclared variable " .. n, 2)
end,
__index = function (_, n)
error("attempt to read undeclared variable " .. n, 2)
end,
})


After this code, any attempt to access a non-existent global variable will trigger
an error:
> print(a)
stdin:1: attempt to read undeclared variable a

--整个程序都退出了,

But how do we declare new variables? One option is to use rawset, which
bypasses the metamethod:


function declare (name, initval)  --每次都这么调用,多麻烦 ,,I do not like it
rawset(_G, name, initval or false)
end

(The or with false ensures that the new global always gets a value different
from nil.)



A simpler option is to restrict assignments to new global variables only inside
functions, allowing free assignments in the outer level of a chunk. (也就是main chunk or C 里面我们可以put global

varaible ,but in function call can not)


To check whether the assignment is in the main chunk, we must use the
debug library. The call debug.getinfo(2,"S") returns a table whose field what

tells whether the function that called the metamethod is a main chunk, a regular
Lua function, or a C function.


setmetatable(_G,{
__newindex=function(_,key,value)

 local w=debug.getinfo(2,"S").what;
  print(w,key,value);
 if w~="C" and  w ~="main" then  --  注意是and 而不是 or , 不等于C 也不能等于main,那说明是Lua ,that will be true.
  error("you can not update a variable in this scope type");
 end

 rawset(_,key,value);  -- if in main or C , 那么可以assigment.
end
});
 
 

function  testfunct()
  k=10;--when run testfunct ,k will put into _G, but we resict the assigment,so will hit error
  local b=10; -- b will not put into to _G, so will not hit error
end

a=10;  --global assigment
testfunct();



============首先我们需要明白下面的概念:

1. c:\lun\bin\ when you run lua command ,enter interative model, 此时会create a main chunk.

> =debug.getinfo(1,"S").what;  --1 ,表明是谁call 这个getinfo 函数的, 当然是在main chunk 里面
main

----- 我们可以这么看在交互模式下的code

void main()

{

  print( =debug.getinfo(1,"S").what;);

}


================


2. lession14.lua

print(debug.getinfo(1,"S").what); --main chunk

function  testfunct()
  a=10;--when run testfunct ,a will put into _G
  local b=10; -- b will not put into to _G
  print("this is who call getinfo:",debug.getinfo(1,"S").what); --in function ,normal function ,that's Lua
  print("this is who call testfunct:",debug.getinfo(2,"S").what); --should be in main chunck
end

testfunct();


>dofile(lession14.lua); -- 在交互模式下运行dofile 时候,会创建a new main chunk .

所以第一print ,当然是在main chunk 里面call 起的

when calltestfunct();, inside the test function. we set getinfo  the first para 1 and 2,

参数1  表明是谁call getinfo 的,那当然是函数testfunct , so will print Lua ,mean it's a normal function

para 2  indicate who calltestfunct, 即getinfor 的上一个函数testfunct 是谁call 起的,那当然是main chunk. so will print main


when dofile lession14.lua will compile this code:

main ()

{

 

print(debug.getinfo(1,"S").what); --main chunk

function  testfunct()
  a=10;--when run testfunct ,a will put into _G
  local b=10; -- b will not put into to _G
  print("this is who call getinfo:",debug.getinfo(1,"S").what); --in function ,normal function ,that's Lua
  print("this is who call testfunct:",debug.getinfo(2,"S").what); --should be in main chunck
end

testfunct();



}


call stack:


main[2] -->testfunct[1]-->debug.getinfo[0]  ,

关键是要明白getinfo 第一个参数的意义...


0 是代表的是getinfo 本身,它是C 来实现调用的,so will return C

1 , means who call debug.getinfo. that will be testfunct, so will return Lua, indicate a normal function

2. means who call debug.getinfo 的上一级堆栈 (testfunct) ,that will be the main chunk.




==========

To test whether a variable exists, we cannot simply compare it to nil because,
if it is nil, the access will throw an error. Instead, we use rawget, which avoids
the metamethod:
if rawget(_G, var) == nil then
-- 'var' is undeclared
...
end


书上说的这段话,意思是由于我们 overwrite 了__index, when access an non exsting variable, will raise error.


===see my demo

setmetatable(_G,{

__index=function(_,key)
   local w=debug.getinfo(2,"S").what;
   if w~="C" and w~="main" then
      error(" can not get a non existing glabal variable in this scope");
   end
   return rawget(_,key);
end,
__newindex=function(_,key,value)

 local w=debug.getinfo(2,"S").what;
  print(w,key,value);
 if w~="C" and  w ~="main" then  -- and not or ar,,,
  error("you can not update a variable in this scope type");
 end

 rawset(_,key,value);
end
});
 
 

function  testfunct()
  k=10;--when run testfunct ,a will put into _G
  local b=10; -- b will not put into to _G
  print("this is who call getinfo:",debug.getinfo(1,"S").what); --in function ,normal function ,that's Lua
  print("this is who call testfunct:",debug.getinfo(2,"S").what); --should be in main chunck
end

--a=10;
--testfunct();

function  testfun()
  print(kkkkk);   --在function  Lua scope 访问一个non existing var will     hit error.
end

print(kkkkk);  -- but in main chunk will not hit.
testfun();

========








====

14.3 Non-Global Environments (Lua5.2)


Global var is a problem, 

lession14-3.lua

hikkj=10;


>dofile("lession14-3.lua"); -- will put hikkj in to _G. when this main chunk exit.

> for k,v in pairs(_G) do print (k,v) end;

打印出来的结果可以看到, 回到 interative model , _G 里面仍然保留了这个hikkj ... 太global 了吧,,在某些情况下会出大问题的。。。。



In Lua, global variables do not need to be truly global. We can even say that
Lua does not have global variables
. 这是对的。

Let us start with the concept of free names. A free name is a name that is
not bound to an explicit declaration, 也就是从来没被定义过的,声明过的.

ie:

lession14-4.lua

myAge=youAge+5;


>dofile "lession14-4.lua";  到底这个dofile 做了什么,看完下面的就知道什么是global,什么是local.

_G={All predefine function, and var.. print,setmetatable};

local _ENV=_G;

void main(...)

{

   _ENV.myAge=_ENV.youAge+5;

}

----myAge  youAge 这些free name,其实最终会被编译器 解析成_ENV.varName.


而关键是main 前面的_EVN this upvalue.  local _ENV 指向了_G.  所以我们定义的变量就等于放到global 上了。

知道了这个过程那么就知道怎么让我们的变量变成local了,即使是在main chunk里面.

1.============lession14-3.lua

MyPage=1000; --here still in global.
print(MyPage);


_ENV={g=_G};  --注意这里不能写成 _ENV={}; _ENV.g=_G, 因为开始的时候 _ENV=_G, 如果先_ENV={}

                      --那么此时_G 将会丢失。

MyAge=100;
youAge=100;  --MyAge youAge 将不时放在_G 上面了,而是我们的local scope _ENV.
for k,v in g.pairs(_ENV) do
g.print(k,v); --因为我们print 是在_G 里面预定义,而_ENV 指向了一empty table{}, so here can not just write 

                   ----print(k,v);
end

g.print(g.MyPage); 


> dofile "lession14-3.lua";

1000

youAge  100
MyAge   100
g       table: 002E1A18

1000

> =youAge
nil   --显然我们实现了local scope in our main chunk



2.=====每次都要g.XXX 多麻烦,我们可以利用继承

-- when start, _ENV=_G.
MyPage=1000; --here still in global.
print(MyPage);
local mt={};
setmetatable(mt,{__index=_G});
_ENV=mt;
MyAge=100;
youAge=100;
for k,v in pairs(_ENV) do
print(k,v);
end
print(MyPage);


=======


local mt={};
setmetatable(mt,{__index=_G});
_ENV=mt;
KB=150;
do
  local _ENV={print=print};
  KB=289; -- No need to define as local KB, because we have redefine _ENV as local.

  print(KB);
end
print(KB);

> dofile"lession14-3.lua";
289   --可见当我们重定义了_ENV 后,, KB是生存在这个local env上的  what a power things
150


==

function factory (_ENV) --其实类似load loadfile 函数也是允许我们出入_ENV参数来构造我们的 local global.

                                          --------see    below:14.5 _ENV and load
    return function ()
        return a -- "global" a , 这个所谓的global,其实是生存在参数factory (local _ENV)上面的
    end
end
f1 = factory{a = 6}  --每一closure 都相当于有了自己的glabal .
f2 = factory{a = 7}
print(f1()) --> 6
print(f2()) --> 7 




============


14.5 _ENV and load

load,loadfile 都有一个可选参数让我们传入_ENV. 利用这个特性我们有下面的用处。

-- file 'config.lua'
width = 200
height = 300
...
We can load it whit the following code:
env = {}
f = loadfile("config.lua", "t", env) --pass a empty _ENV .
f() --不管运行多少次  f 里面的 global 始终share 同一个_ENV that's what we pass env={}; 因为我们传入了这个empty{} , so in config.lua 将会last _G 里面pre-define的东西。。。也就是纯粹的 config 文件了.


loadfile(filename,model,env);

Similar to load,but gets the chunk from file filenameor from the standard input,if no file name is given.

model para: The string mode controls whether the chunk can be text or binary(that is, a precompiled chunk).It may be the string "b" (only binary chunks),"t" (only text chunks),or "bt" (both binary and text).The default is "bt".


某些情况下share 同一个 env is good.但有时候我们想 every time we excute a chunk (load,loadfile return a new chunk), it use a different  env. how we

can do that:

1. use debug.setupvalue

f = loadfile(filename)
...
env = {}
debug.setupvalue(f, 1, env) -- 把f 里面 第一个index 的变量替换为 env. 第一index 的变量是什么? always _ENV.

so after that when we call f() again, it will use a new env .


The first argument in the call to setupvalue is the function, the second is the
upvalue index, and the third is the new value for the upvalue. For that kind of
use, the second argument is always one: when the function is the result of load
or loadfile,
(也就是通过load,loadfile 返回来的chunk, luan 保证第一index 是_ENV.) Lua ensures that it has only one upvalue and that this upvalue is  _ENV.




2.Remember from Section 8.1 that Lua compiles any chunk as a variadic function. each compile chunk 参数都是可变的, 也就是void main(...){} , 语法 ...  indicate its variadic parameter.

利用这个我们可以这样:

lession14.lua

local _ENV=...;  -- 加不加local 其实都没关系了,因为他都是第一个了,相对下面的来说已经是global了。

a=10;

b=100;

myb=100;


>myenv1={};

>f=loadfile("lession14.lua","t");  -- no need to pass a env in the loadfile

>f(myenv1); -- we need to passmyenv1,orelse wil hit error,when excute _ENV=...;

>=myenv1.a;

>myenv2={};

>f(myenv2);

>=myenv2.a;



====

Exercise 14.2: Explain in detail what happens in the following program and
what its output is.   这个练习值得在这里voice out 出来,确实很巧妙:


local foo;  -- 注意拉,foo is a local variable.
do
   local _ENV = _ENV --这句很重要,否则在_ENV=nil 的时候, 在access X 将无法访问到
   function foo () print(X) end -- 定义上面声明的local foo .
end
X = 13   --global env.
_ENV = nil --- then we lose the  reference of the global env. X=13 will never access by now.
foo()  -- 由于我们的redefine local _ENV=_ENV. so when call print(X), will parse 成local _ENV.X, 而local _ENV is the global ENV. , 相当于foo 形成了enclosure. refere 住了local _ENV
X = 0  -- 由于_ENV=nil, 其实将无法访问 global 的 _ENV ,so here will hit error.

--lession14-3.lua:3: attempt to index upvalue '_ENV' (a nil value)


===need to refere below example:

a=10;
local a=100;
  do
     a=200; --a 都是上面定义的local a.
     print(a); --a
  end
print(a);   -- 还是local a. local a 隐藏了a=10 global variable.
print(_ENV.a);



Exercise 14.3: Explain in detail what happens in the following program and
what its output is.
local print = print
function foo (_ENV, a)
print(a + b)
end
foo({b = 14}, 12)  --26
foo({b = 10}, 1)  --11








































 



































评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值