• chapter 13_4 跟踪table的访问


      __index和__newindex都是在table中没有所需访问的index时才发挥作用。

    因此,只有将一个table保持为空,才有可能捕捉到所有对它的访问。为了监视一个table的所有访问,就应该为真正的table创建一个代理。

    这个代理就是一个空的table,其中__index和__newindex元方法可用于跟踪所有的访问,并将访问重定向到原来的table上。

    假设我们要跟踪table t 的访问,可以这样做:

    t  = {}          --original table, 在其他地方创建的
    local _t = t     --保持对原table的一个私有访问
    t = {}           --创建一个代理
    local mt = {
                __index = function (t,k)
                            print("*access to elemet " .. tostring(k))
                            return _t[k]      --访问原table中的k
                        end
                __newindex = function(t,k,v)
                            print("*update of element" .. tostring(k) .. " to " .. tostring(v))
                            _t[k] = v        --更新原table的值
                        end
    }
    setmetatable(t,mt)    --将mt设置为 t 的元表

    这段代码跟踪了所有对 table t 的访问:

    t[2] = "hello"              -- *update of element 2 to hello
    print(t[2])       --> hello        -- *access to element 2

    但是上面的例子有一个问题,就是无法遍历原来的table。函数pairs只能操作代理table,而无法访问原来的table。

    可以通过定义__pairs去遍历:

    mt.__pairs = function()
        return function(_,k)
            return next(_t,k)
            end
    end

      如果想要同时监视几个table,无须为每个table创建不同的元表。相反,只要以某种形式将每个代理与其原table关联起来,

    并且所有代理都共享一个公共的元表。这个问题与前一节所讨论的将table与其默认值相关联的问题类似。

    例如将原来table保存在代理table的一个特殊字段中,如下:

    local index = {}        --创建私有索引
    local mt = {            --创建元表
        __index = function(t,k)
                print("*access to element " .. tostring(k)
                return t[index][k]    --访问原来的table
            end
        __newindex = function(t,k,v)
                print("*update of element " .. tostring(k) .. " to " .. tostring(v))
                t[index][k] = v        --更新原来的table
            end
        __pairs = function(t)
                return function(t,k)
                    return next(t[index],k)
                end , t
            end
    }
    
    function track(t)
        local proxy= {}
        proxy[index] = t
        setmetatable(proxy,mt)
        return proxy
    end

    现在,若要监视table t ,唯一要做的就是执行:

    t = track(t)

    只读的table

      通过代理的概念,可以很容易地就实现出只读的table,只需跟踪所有对table的跟新操作,并引发一个错误就可以了。

    由于无须跟踪查询访问,所以对于__index元方法可以直接使用原table来代替函数。这也更简单,并且在重定向所有查询到原table时效率也更高。

    不过,这种做法要求为每个只读代理创建一个新的元表,其中__index指向原来的table。

    function readOnly(t)
        local proxy = {}
        local mt = {
            __index =t ,    
            __newindex = function(t,k,v)
                            error("*attempt to update a read-only table",2)
                        end
        }
        setmetatable(proxy,mt)
        return proxy
    end

    下面是一个使用的示例,创建了一个表示星期的只读table:

    days = readOnly{"Sunday","Monday","Tuesday","Thursday","Friday","Saturday"}
    print(days[1])        -->Sunday
    days[2] = "Noday"     -- > stdin:1: attempt to update a read-only table

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

  • 相关阅读:
    Mybatis源码中最重要的几个类
    学习爬虫-运营商积分
    IntelliJ IDEA 最新版 2019.2.4 激活 (持续更新)(含windows和Mac)
    归并排序之求小和
    归并排序
    理解递归
    插入排序
    对数器
    冒泡排序
    mysql 数据库名称,中间带有中划线问题
  • 原文地址:https://www.cnblogs.com/daiker/p/5852283.html
Copyright © 2020-2023  润新知