zoukankan      html  css  js  c++  java
  • Chapter 14_2 全局变量声明

      Lua中的全局变量不需要声明就可以使用。对于小程序十分方便,但是大型程序中 一处简单的笔误就可能造成难以发现的bug。

    不过,这种性能可以改变。由于Lua将全局变量放在一个普通的table中,可以通过元表来改变其访问全局变量时的行为。

    一种方法是简单地检测所有对全局table中不存在key的访问:

    setmetatable(_G,{
            __newindex = function(_, n)
                            error("attempt to write to undeclared variable " .. n ,2)
                        end ,    --这里的逗号不能省,因为是在table构造式里哦
            __index = function(_, n)
                            error("attempt to read undeclared variable " .. n, 2)
                        end ,   --这里的逗号不能省,因为是在table构造式里哦   
                }
    )

    执行这段代码后,所有对全局table中不存在的key访问都将引发一个错误:

    print(a)        --> stdin:1: attempt to read undeclared variable a

    但是该如何声明一个新的变量呢?

    一种方法是使用rawset,可以绕过元表:

    function declare(name ,initval)
        rawset(_G,name ,initval or false)    --这里的false确保 新变量不为 nil
    end

    另外一中更简单的方法就是只允许在主程序块中对全局变量进行赋值,那么当声明以下变量时:

    a  =  1

    就只需检查此赋值是否在主程序块中。这可以使用debug库,调用debug.getinfo(2,"S")将返回一个table,

    其中的字段what表示了调用元方法的函数是主程序块还是普通的Lua函数,或是C函数。

    可以通过该函数将__newindex元方法重写:

    __newindex = function(t,n,v)
        local w = debug.getinfo(2,"S").what
        if w ~= "main" and w ~= "C" then  --接收main和C代码的修改赋值
            error("attempt to write to undeclared variable ".. n,2)
        end
        rawset(t,n,v)
    end

    这个版本可以接收C代码的赋值,因为一般C代码都知道自己是做什么的。

    为了测试一个变量是否存在,就不能简单地与nil 比较。因为如果它为nil ,访问就会抛出一个错误,这时同样可以使用rawget来绕过元方法:

    if rawget(_G , var ) ==nil then
        -- 'var' 没有声明
        ...
    end

    正如前面提到的,不允许全局变量具有nil 值,因为具有nil 的全局变量都会被自动认为是未声明的。

    但要纠正这个问题并不难,只需引入一个辅助table用于保存已经声明变量的名称。

    一旦调用了元方法,就检查该table,以确定变量是否已经声明过:

    local declaredNames = {}
    setmetatable( _G , {
            __newindex = function( t , n, v)
                            if not declaredNames[n] then
                                local w = debug.getinfo(2,"S").what
                                if w ~= "main" and w ~= "C" then
                                    error("attempt to write to undeclared variable " .. n, 2 )
                                end
                                declaredNames[n] = true
                            end
                                rawset(t , n ,v )    --完成实际的设置
                        end , 
            __index = function (_, n)
                        if not declaredNames[n] then
                            error("attempt to read undeclared variable " .. n, 2 )
                        else
                            return nil
                        end
                    end ,
                }
    )

    此时,即使是下面的赋值也可以起到声明全局变量的作用:

    x = nil

    上述两中方法所导致的开销基本可以忽略不计;

    第一种方法中,完全没有涉及到元方法的调用。

    第二种方法,只有当程序访问一个为nil的变量时才会去调用元方法。

    以上内容来自:《Lua程序设计第二版》和《Programming in Lua  third edition》

  • 相关阅读:
    hihoCoder #1176 : 欧拉路·一 (简单)
    228 Summary Ranges 汇总区间
    227 Basic Calculator II 基本计算器II
    226 Invert Binary Tree 翻转二叉树
    225 Implement Stack using Queues 队列实现栈
    224 Basic Calculator 基本计算器
    223 Rectangle Area 矩形面积
    222 Count Complete Tree Nodes 完全二叉树的节点个数
    221 Maximal Square 最大正方形
    220 Contains Duplicate III 存在重复 III
  • 原文地址:https://www.cnblogs.com/daiker/p/5854215.html
Copyright © 2011-2022 走看看