zoukankan      html  css  js  c++  java
  • chapter 13_3 table访问的元方法

      前两节的算术类、关系类运算符的元方法都为各种错误情况定义了行为,它们不会改变语言的常规行为。

    但是Lua还提供了两种可以改变table行为的方法:

    一种是查询table中不存在的字段、一种是修改table中不存在的字段。

    __index元方法

    当访问一个table中不存在的字段时,得到的结果为nil。这样的访问会促使解释器去查找一个叫__index的元方法。

    如果没有这个元方法,那么访问结果就是nil。

    一个有关继承的示例:假设要创建一些描述窗口的table,每个table中必须表述一些窗口参数,如:位置、大小、主题....

    所有这些参数都有默认值,因此希望在创建窗口对象时可以仅提供那些不同于默认值的参数。

    第一种方法是使用一个构造式,在其中填写那些不存在的字段。

    第二种方法是让新窗口从一个原型窗口处继承所有不存在的字段。

    首先,声明一个原型和一个构造函数,构造函数创建新的窗口,并使它们共享同一个元表:

    prototype = {x = 0, y = 0,width = 100 , height  = 100} --用默认值来创建一个原型
    mt = {}            --创建一个元表
    function new(o)    --声明构造函数
        setmetatable(o,mt)
        return o
    end

    现在,定义__index元方法:

    mt.__index = function(_,key)
        return prototype[key]
    end

    在这段代码之后,创建一个新窗口,并查询一个它没有的字段:

    w = new{x=10,y=20}
    print(w.width)    -->100

    若Lua检测到w中没有width字段,但在其元素中却有一个__index字段,那么Lua就会以 w 和 “width” 来调用这个__index元方法。

    随后元方法用这个key来索引原型table,并返回结果。

    在Lua中,将__index元方法用于继承是很普遍的方法,因此Lua还提供了一种更便捷的方式来实现此功能。

    __index不一定是一个函数,可以是一个table。

    当它是一个table时,Lua就以相同的方式来重新访问这个table。因此,上面的__index的声明可以写为:

    mt.__index = prototype

    现在,Lua查到元素__index时,发现它是一个table,那么Lua就会在prototype中继续查找。

    也就是说,Lua会在这个table中国重复这个访问过程:类似于这样访问

    prototype["width"]

    这样将一个table作为__index元方法 是快捷的、实现单一继承的方式。虽然用函数更加灵活,但是开销较大。

    可以通过函数来实现多重继承、缓存及其他一些功能。

    如果不想在访问一个table时涉及到它的__index元方法,可以使用rawget函数。

    调用rawget(t , i) 就是对table t 进行了一个原始的访问。

    __newindex元方法

    它与__index类似,不同之处在于它用于table的更新,而__index用于table的查询。

    当对一个table中不存在的索引赋值时,解释器就会查找__newindex元方法。如果有这个元方法,解释器就调用它,而不是执行赋值。

    如果这个元方法是一个table,解释器就在此table中执行赋值,而不是对原来的table。

    也可以通过rawset( t ,k , v )来绕过元方法,直接设置table  t 中与key k 相关联的value v 

    组合使用__index和__newindex元方法就可以实现出Lua中的一些强大功能,例如:

    只读的table、具有默认值的table、面向对象编程中的继承。

    具有默认值的table

    常规table中的任何字段默认值都是nil,通过元表就可以很容易地修改这个默认值:

    function setDefault(t,d)
        local mt = {__index = function() return d end}
        setmetatable(t,mt)
    end
    
    tab = {x=10,y=20}
    print(tab.x,tab.z)    --> 10 nil
    setDefault(tab, 1)
    print(tab.x,tab.z)    -->10 1

    在调用setDefault后,任何对tab中不存在的访问都将调用它的__index元方法,而这个元方法会返回d的值。

    setDefault函数为所有需要默认值的table创建了一个新的元素。

    如果准备创建很多需要默认值的table,这个开销比较大。

    由于在元表中默认值d是与元方法关联在一起的,所以setDefault无法为所有table都使用同一个元表。

    若要让具有不同默认值的table都使用同一个元表,那么需要将每个元表的默认值都放到table本身中。

    可以使用一个额外的字段来保存默认值。避免冲突,可以使用"___"这样的key作为额外的字段:

    local mt = {__index = funxtion (t) return t.___ end}
    function setDefault(t, d )
        t.___ = d
        setmetatable(t,mt)
    end

    如果担心名称冲突,那么要确保这个特殊key的唯一性也很容易,只需创建一个新的额table,并用它作为key即可:

    local key = {}  --唯一的key
    local mt = {__index = function(t) return t[key]  end}
    function setDefault(t , d)
        t[key] =d
        setmetatable(t,mt)
    end

    还有一种方法可以将table与其默认值关联起来:使用一个独立的table,它的key为各种table,value就是各种table的默认值。

    不过,为了正确地实现这种做法,需要一种特殊性质的table,就是“弱引用table”。

    还有一种备忘录元表的方法,它能使具有相同默认值的table复用同一个元表,也需要用到弱引用table。以后会讲到.....

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

  • 相关阅读:
    Linux初级入门(一)
    教育行业----精华网址汇总
    乐高教育版45544零件---分类识别
    乐高ev3编程---书籍选择
    机器人 乐高ev3 arduino 控制器等
    scratch----回家过年
    Scratch2.0例—接苹果
    scratch2绘图编辑器使用--总结
    在舞台上跳舞
    教学设计--我是飞行员
  • 原文地址:https://www.cnblogs.com/daiker/p/5850687.html
Copyright © 2011-2022 走看看