zoukankan      html  css  js  c++  java
  • Programming in lua 环境

    Lua 用一个名为environment 普通的表来保存所有的全局变量。(更精确的说,Lua
    在一系列的environment 中保存他的“global”变量,但是我们有时候可以忽略这种多样
    性)这种结果的优点之一是他简化了Lua 的内部实现,因为对于所有的全局变量没有必
    要非要有不同的数据结构。另一个(主要的)优点是我们可以像其他表一样操作这个保存
    全局变量的表。为了简化操作,Lua 将环境本身存储在一个全局变量_G 中,(_G._G 等
    于_G)。例如,下面代码打印在当前环境中所有的全局变量的名字:
    for n in pairs(_G) do print(n) end
    这一章我们将讨论一些如何操纵环境的有用的技术。

       1. 全局变量声明:
        Lua中的全局变量不需要声明就可以使用。尽管很方便,但是一旦出现笔误就会造成难以发现的错误。我们可以通过给_G表加元表的方式来保护全局变量的读取和设置,这样就能降低这种笔误问题的发生几率了。见如下示例代码:

    --该table用于存储所有已经声明过的全局变量名
    local declaredNames = {} 
    local mt = {
        __newindex = function(table,name,value)
            --先检查新的名字是否已经声明过,如果存在,这直接通过rawset函数设置即可。
            if not declaredNames[name] then
                --再检查本次操作是否是在主程序或者C代码中完成的,如果是,就继续设置,否则报错。
                local w = debug.getinfo(2,"S").what
                if w ~= "main" and w ~= "C" then
                    error("attempt to write to undeclared variable " .. name)
                end
                --在实际设置之前,更新一下declaredNames表,下次再设置时就无需检查了。
                declaredNames[name] = true
            end
            print("Setting " .. name .. " to " .. value)
            rawset(table,name,value)
        end,
        
        __index = function(_,name)
            if not declaredNames[name] then
                error("attempt to read undeclared variable " .. name)
            else
                return rawget(_,name)
            end
        end
    }    
    setmetatable(_G,mt)
    
    a = 11
    local kk = aa
    
    --输出结果为:
    --[[
    Setting a to 11
    lua: d:/test.lua:21: attempt to read undeclared variable aa
    stack traceback:
            [C]: in function 'error'
            d:/test.lua:21: in function <d:/test.lua:19>
            d:/test.lua:30: in main chunk
            [C]: ?
    --]]

      2. 非全局的环境:
        全局环境存在一个刚性的问题,即它的修改将影响到程序的所有部分。Lua 5为此做了一些改进,新的特征可以支持每个函数拥有自己独立的全局环境,而由该函数创建的closure函数将继承该函数的全局变量表。这里我们可以通过setfenv函数来改变一个函数的环境,该函数接受两个参数,一个是函数名,另一个是新的环境table。第一个参数除了函数名本身,还可以指定为一个数字,以表示当前函数调用栈中的层数。数字1表示当前函数,2表示它的调用函数(这对写一个辅助函数来改变他们调用者的环境是很方便的),以此类推。见如下代码:

    下面这段代码是企图应用setfenv 失败的例子:
    a = 1 -- create a global variable
    -- change current environment to a new empty table
    setfenv(1, {})
    print(a)
    导致:
    stdin:5: attempt to call global `print' (a nil value)
    (你必须在单独的chunk 内运行这段代码,如果你在交互模式逐行运行他,每一行
    都是一个不同的函数,调用setfenv 只会影响他自己的那一行。)一旦你改变了你的环境,
    所有全局访问都使用这个新的表,如果她为空,你就丢失所有你的全局变量,甚至_G,
    所以,你应该首先使用一些有用的值封装(populate)她,比如老的环境:

    a = 1 -- create a global variable
    -- change current environment
    setfenv(1, {_G = _G})
    _G.print(a) --> nil
    _G.print(_G.a) --> 1

    现在,当你访问"global" _G,他的值为旧的环境,其中你可以使用print 函数。

     
    你也可以使用继承封装(populate)你的新的环境
    a = 1
    local newgt = {} -- create new environment
    setmetatable(newgt, {__index = _G})
    setfenv(1, newgt) -- set it
    print(a) --> 1
    这段代码新的环境从旧的环境中继承了print 和a;然而,任何赋值操作都对新表
    进行,不用担心误操作修改了全局变量表。另外,你仍然可以通过_G 修改全局变量:

    -- continuing previous code
    a = 10
    print(a) --> 10
    print(_G.a) --> 1
    _G.a = 20
    print(_G.a) --> 20
    当你创建一个新的函数时,他从创建他的函数继承了环境变量。所以,如果一个
    chunk 改变了他自己的环境,这个chunk 所有在改变之后定义的函数都共享相同的环境,
    都会受到影响。这对创建命名空间是非常有用的机制,我们下一章将会看到。

  • 相关阅读:
    React: React的组件状态机制
    React: React的复合组件
    JavaScript:ES6的新特性
    React: 研究React的组件化
    React: 认识React
    CSS:CSS弹性盒子布局 Flexible Box
    iOS:应用程序扩展开发之Today扩展(Today Extesnsion)
    《逆向工程核心原理》
    《左手数据,右手图表》
    《设计模式之禅(第2版)》
  • 原文地址:https://www.cnblogs.com/youxin/p/3805187.html
Copyright © 2011-2022 走看看