码迷,mamicode.com
首页 > 其他好文 > 详细

Metatable和Metamethod

时间:2014-07-11 10:56:41      阅读:185      评论:0      收藏:0      [点我收藏+]

标签:style   blog   color   使用   strong   os   

根据Metatable的用法,我倾向于将Metatable翻译成关联表,Metamethod翻译成关联函数

通过给两个table设置同一个Metatable可以使两个table产生联系,将两个table关联起来,然后对两个table进行一些操作,具体的操作行为由Metamethod来定义。

下面是一个使用关联表来对集合(用table实现的集合)进行操作的示例,实例中定义了集合的并集、交集、比较等运行:

Set = {}

--专门用来作为metatable,定义在Set里面以免影响外部的命名空间
Set.mt = {}    

--转化为string
Set.tostring = function (set)
    local s = "{"
    local sep = " "
    for e in pairs(set) do
        s = s .. sep .. e
        sep = ", "
    end
    return s.."}"
end

--打印
Set.print = function(s)
    print(Set.tostring(s))
end

Set.mt.__tostring = Set.tostring

--新建一个集合
Set.new = function (t)
    local set = {}
    setmetatable(set, Set.mt)        --指定所创建集合的metatable
    for _, l in ipairs(t) do set[l] = true end
    return set
end

--并集
Set.union = function (a,b)
    local res = Set.new{}
    for k in pairs(a) do res[k] = true end
    for k in pairs(b) do res[k] = true end
    return res
end

--给metatable增加__add函数(metamethod),当Lua试图对两个集合相加时,将调用这个函数,以两个相加的表作为参数
Set.mt.__add = Set.union

--交集
Set.intersection = function (a,b)
    local res = Set.new{}
    for k in pairs(a) do
        res[k] = b[k]
    end
    return res
end
--定义集合相乘操作为求交集
Set.mt.__mul = Set.intersection

--先定义"<="操作,然后基于此定义"<"和"="
Set.mt.__le = function (a, b)
    for k in pairs(a) do
        if not b[k] then return false end
    end
    return true
end

--小于
Set.mt.__lt = function(a, b)
    return a<=b and not (b <= a)
end

--等于
Set.mt.__eq = function(a, b)
    return a <= b and b <= a
end

--测试
s1 = Set.new{1, 2, 3}
s2 = Set.new{10, 20, 30, 40, 50}
print(getmetatable(s1))
print(getmetatable(s2))
s3 = s1 + s2    --等同于Set.union(s1, s2)
print(s3)
print(s3 * s2)

print(s1 <= s3)
print(s1 == s3)
print(s1 < s3)
print(s1 >= s3)
print(s1 > s3)

--起保护作用,getmetatable将返回这个域的值,而setmettable将会出错
Set.mt.__metatable = "not your business"

print(getmetatable(s1))
setmetatable(s1, {})

  当Lua试图对两个表进行相加时,他会检查两个表是否有一个表有Metatable,并且检查Metatable是否有__add域。如果找到则调用这个__add函数(所谓的Metamethod)

计算结果。当两个表有不同的Metatable时,以谁的为准呢?Lua选择metamethod的原则:

  (1)如果第一个参数存在带有__add域的metatable,Lua使用它作为metamethod,和第二个参数无关;

  (2)否则,第二个参数存在带有__add域的metatable,Lua使用它作为metamethod;

  (3)否则,报错。

  Lua中定义的常用的Metamethod如下所示:

  算术运算符的Metamethod:__add(加运算)、__mul(乘)、__sub(减)、__div(除)、__unm(负)、__pow(幂),__concat(定义连接行为)。

  关系运算符的Metamethod:__eq(等于)、__lt(小于)、__le(小于等于),其他的关系运算自动转换为这三个基本的运算。

  库定义的Metamethod:__tostring、__metatable

  假定你想保护你的集合使其使用者既看不到也不能修改metatables。如果你对metatable设置了__metatable的值getmetatable将返回这个域的值,而调用用setmetatable将会出错:

  注意:相等比较从来不会抛出错误,如果两个对象有不同的metamethod,比较的结果为false,甚至可能不会调用metamethod。这也是模仿了Lua的公共的行为,因为Lua总是认为字符串和数字是不等的,而不去判断它们的值。仅当两个有共同的metamethod的对象进行相等比较的时候,Lua才会调用对应的metamethod。

  print总是调用tostring来格式化它的输出,tostring会首先检查对象是否存在一个带有__tostring域的metatable。

 

Metatable和Metamethod,布布扣,bubuko.com

Metatable和Metamethod

标签:style   blog   color   使用   strong   os   

原文地址:http://www.cnblogs.com/sifenkesi/p/3834128.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!