zoukankan      html  css  js  c++  java
  • lua 元表与元方法示例

    -- 1、检查是否有元表
    local t = {1, 2}
    print(getmetatable(t))     -- nil
    print("----------------------")
    -- 2、设置元表
    local t = {}
    print(getmetatable(t))     -->nil

    local t1 = {}
    setmetatable(t, t1)
    print (getmetatable(t))
    assert(getmetatable(t) == t1)
    print("----------------------")
    -- 3、常用元方法
    --[[
    __add(a, b) --加法
    __sub(a, b) --减法
    __mul(a, b) --乘法
    __div(a, b) --除法
    __mod(a, b) --取模
    __pow(a, b) --乘幂
    __unm(a) --相反数
    __concat(a, b) --连接
    __len(a) --长度
    __eq(a, b) --相等
    __lt(a, b) --小于
    __le(a, b) --小于等于
    __index(a, b) --索引查询
    __newindex(a, b, c) --索引更新(PS:不懂的话,后面会有讲)
    __call(a, ...) --执行方法调用
    __tostring(a) --字符串输出
    __metatable --保护元表
    --]]

    local tmeta_complext= {}
    local TComplex={}
    function TComplex.new(a,b)
        tComplex = {a,b}
        setmetatable(tComplex, tmeta_complext)
        return tComplex
    end

    function TComplex.add(c1,c2)
        tcomplext = {0,0}
        setmetatable(tcomplext, tmeta_complext)
        if c1 and c1[1] and type(c1[1]) == "number" then
            tcomplext[1] = tcomplext[1] + c1[1]
        end
        if c1 and c1[2] and type(c1[2]) == "number" then
            tcomplext[2] = tcomplext[2] + c1[2]
        end
        if c2 and c2[1] and type(c2[1]) == "number" then
            tcomplext[1] = tcomplext[1] + c2[1]
        end
        if c2 and c2[2] and type(c2[2]) == "number" then
            tcomplext[2] = tcomplext[2] + c2[2]
        end
        return tcomplext
    end

    function TComplex.concat(c1,c2)
        return TComplex.add(c1,c2)
    end

    function TComplex.tostring(c1)
        return "{" ..c1[1]..","..c1[2].."}"
    end

    function TComplex.print(t)
        print (TComplex.tostring(t))
    end


    tmeta_complext.__add = TComplex.add
    tmeta_complext.__tostring = TComplex.tostring
    tmeta_complext.__index = function(t, k)
        return "hei..hei, not accessiable"
    end
    tmeta_complext.__newindex = function (t, k, v)
        print("Attempt to update a read-only field.")
    end
    local t3 = TComplex.new(3,1)
    local t4 = TComplex.new(4,1)

    t4 = t3 + t4
    TComplex.print(t4)
    print (t4)
    print (TComplex.tostring(t3))
    print (t3[1],t3[6])
    t3[6] = 7
    print (t3[1],t3[6])

    metatable是Lua中的重要概念。每一个table都可以加上metatable。meatable可以改变相应的table的行为。让我们看一个例子:

    t = {} -- 普通的table
    mt = {} -- metatable
    setmetatable(t, mt) -- 设定mt为t的metatable
    getmetatable(t) -- 返回mt
    

    使用 getmetatablesetmetatable 来查看和设定metatable。当然,上面的代码也可以压缩成一行:

    t = setmetatable({}, {})
    

    这是因为 setmetatable 会返回它的第一个参数。

    metatable可以包括任何东西,metatable特有的键一般以 __ 开头,例如 __index__newindex ,它们的值一般是函数或其他table。

    t = setmetatable({}, {
      __index = function(t, key)
        if key == "foo" then
          return 0
        else
          return table[key]
        end
      end
    })
    

    __index

    这是metatable最常用的键了。

    当你通过键来访问table的时候,如果这个键没有值,那么Lua就会寻找该table的metatable(假定有metatable)中的 __index 键。如果 __index 包含一个表格,Lua会在表格中查找相应的键。

    other = { foo = 3 }
    t = setmetatable({}, { __index = other })
    t.foo -- 3
    t.bar -- nil
    

    如果 __index 包含一个函数的话,Lua就会调用那个函数,table和键会作为参数传递给函数。

    __newindex

    类似 __index__newindex 的值为函数或table,用于按键赋值的情况。

    other = {}
    t = setmetatable({}, { __newindex = other })
    t.foo = 3
    other.foo -- 3
    t.foo -- nil
    
    t = setmetatable({}, {
      __newindex = function(t, key, value)
        if type(value) == "number" then
          rawset(t, key, value * value)
        else
          rawset(t, key, value)
        end
      end
    })
    
    t.foo = "foo"
    t.bar = 4
    t.la = 10
    t.foo -- "foo"
    t.bar -- 16
    t.la -- 100
    

    上面的代码中使用了 rawgetrawset 以避免死循环。使用这两个函数,可以避免Lua使用 __index__newindex

    运算符

    利用metatable可以定义运算符,例如 *

    t = setmetatable({ 1, 2, 3 }, {
      __mul = function(t, other)
        new = {}
    
        for i = 1, other do
          for _, v in ipairs(t) do table.insert(new, v) end
        end
    
        return new
      end
    })
    
    t = t * 2 -- { 1, 2, 3, 1, 2, 3 }
    

    __index__newindex 不同, __mul 的值只能是函数。与 __mul 类似的键有:

    • __add (+)
    • __sub (-)
    • __div (/)
    • __mod (%)
    • __unm 取负
    • __concat (..)
    • __eq (==)
    • __lt ( < )
    • __le ( <= )

    __call

    __call 使得你可以像调用函数一样调用table:

    t = setmetatable({}, {
      __call = function(t, a, b, c, whatever)
        return (a + b + c) * whatever
      end
    })
    
    t(1, 2, 3, 4) -- 24
    

    这是很有用的特性。需要以直接调用table的形式调用table中的某个(默认)函数的时候,使用 __call 设定很方便。例如, kikitotween.lua ,就用了这个技巧,这样直接调用 tween 就可以调用 tween.start 。再如 MiddleClass 中,类的 new 方法可以通过直接调用类的方式调用。

    __tostring

    最后讲下 __tostring ,它可以定义如何将一个table转换成字符串,经常和 print 配合使用,因为默认情况下,你打印table的时候会显示 table: 0x<16进制数字>

    t = setmetatable({ 1, 2, 3 }, {
      __tostring = function(t)
        sum = 0
        for _, v in pairs(t) do sum = sum + v end
        return "Sum: " .. sum
      end
    })
    
    print(t) -- prints out "Sum: 6"
    

    例子

    综合运用以上知识,我们编写一个2D矢量类:

    Vector = {}
    Vector.__index = Vector
    
    function Vector.__add(a, b)
      if type(a) == "number" then
        return Vector.new(b.x + a, b.y + a)
      elseif type(b) == "number" then
        return Vector.new(a.x + b, a.y + b)
      else
        return Vector.new(a.x + b.x, a.y + b.y)
      end
    end
    
    function Vector.__sub(a, b)
      if type(a) == "number" then
        return Vector.new(b.x - a, b.y - a)
      elseif type(b) == "number" then
        return Vector.new(a.x - b, a.y - b)
      else
        return Vector.new(a.x - b.x, a.y - b.y)
      end
    end
    
    function Vector.__mul(a, b)
      if type(a) == "number" then
        return Vector.new(b.x * a, b.y * a)
      elseif type(b) == "number" then
        return Vector.new(a.x * b, a.y * b)
      else
        return Vector.new(a.x * b.x, a.y * b.y)
      end
    end
    
    function Vector.__div(a, b)
      if type(a) == "number" then
        return Vector.new(b.x / a, b.y / a)
      elseif type(b) == "number" then
        return Vector.new(a.x / b, a.y / b)
      else
        return Vector.new(a.x / b.x, a.y / b.y)
      end
    end
    
    function Vector.__eq(a, b)
      return a.x == b.x and a.y == b.y
    end
    
    function Vector.__lt(a, b)
      return a.x < b.x or (a.x == b.x and a.y < b.y)
    end
    
    function Vector.__le(a, b)
      return a.x <= b.x and a.y <= b.y
    end
    
    function Vector.__tostring(a)
      return "(" .. a.x .. ", " .. a.y .. ")"
    end
    
    function Vector.new(x, y)
      return setmetatable({ x = x or 0, y = y or 0 }, Vector)
    end
    
    function Vector.distance(a, b)
      return (b - a):len()
    end
    
    function Vector:clone()
      return Vector.new(self.x, self.y)
    end
    
    function Vector:unpack()
      return self.x, self.y
    end
    
    function Vector:len()
      return math.sqrt(self.x * self.x + self.y * self.y)
    end
    
    function Vector:lenSq()
      return self.x * self.x + self.y * self.y
    end
    
    function Vector:normalize()
      local len = self:len()
      self.x = self.x / len
      self.y = self.y / len
      return self
    end
    
    function Vector:normalized()
      return self / self:len()
    end
    
    function Vector:rotate(phi)
      local c = math.cos(phi)
      local s = math.sin(phi)
      self.x = c * self.x - s * self.y
      self.y = s * self.x + c * self.y
      return self
    end
    
    function Vector:rotated(phi)
      return self:clone():rotate(phi)
    end
    
    function Vector:perpendicular()
      return Vector.new(-self.y, self.x)
    end
    
    function Vector:projectOn(other)
      return (self * other) * other / other:lenSq()
    end
    
    function Vector:cross(other)
      return self.x * other.y - self.y * other.x
    end
    
    setmetatable(Vector, { __call = function(_, ...) return Vector.new(...) end })
    
  • 相关阅读:
    Mybatis-plus学习笔记(一)
    Mysql基础(四)分组查询及连接查询
    Mysql 基础(三)排序查询及常用函数
    CyclicBarrier 使用详解
    countDownLatch
    pom所有依赖version红色但是不影响运行
    iText5实现Java生成PDF文件完整版
    【Maven】---Nexus私服配置Setting和Pom
    引用、指针、const、define、static、sizeof、左值右值
    事物隔离级别、MVCC以及数据库中常见锁介绍
  • 原文地址:https://www.cnblogs.com/freebird92/p/4712762.html
Copyright © 2011-2022 走看看