Lua元表与元方法

Lua元表与元方法,第1张

概述前言 元表对应的英文是metatable,元方法是metamethod。我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了“+”符号,就可以进行类的加法运算。在Lua中也有这个道理,两个table类型的变量,你是无法直接进行“+” *** 作的,如果你定义了一个指定的函数,就可以进行了。 Lua是怎么做的? 通常,Lua中的每个类型的值都有一套预定义的 *** 作集合,比如数字是可以相加的,字符串 @H_502_7@前言

@H_502_7@元表对应的英文是Metatable,元方法是Metamethod。我们都知道,在C++中,两个类是无法直接相加的,但是,如果你重载了“+”符号,就可以进行类的加法运算。在Lua中也有这个道理,两个table类型的变量,你是无法直接进行“+” *** 作的,如果你定义了一个指定的函数,就可以进行了。

@H_502_7@Lua是怎么做的?

@H_502_7@通常,Lua中的每个类型的值都有一套预定义的 *** 作集合,比如数字是可以相加的,字符串是可以连接的,但是对于两个table类型,则不能直接进行“+” *** 作。这需要我们进行一些 *** 作。在Lua中有一个元表(Metastable),我们可以通过元表来修改一个值得行为,使其在面对一个非预定义的 *** 作时执行一个指定的 *** 作。比如,现在有两个table类型的变量a和b,我们可以通过Metatable定义如何计算表达式a+b,具体的在Lua中是按照以下步骤进行的:

@H_502_7@先判断a和b两者之一是否有元表; @H_502_7@检查该元表中是否有一个叫__add的字段; @H_502_7@如果找到了该字段,就调用该字段对应的值,这个值对应的是一个Metamethod; @H_502_7@调用__add对应的Metamethod计算a和b的值。

@H_502_7@上述四个步骤就是计算table类型变量a+b的过程。在Lua中,每个值都有一个元表,table和userdata类型的每个变量都可以有各自独立的元表,而其他类型的值则共享其类型所属的单一元表。任何一个表都可以是其他一个表的Metatable,一组相关的表可以共享一个Metatable(描述他们共同的行为)。一个表也可以是自身的Metatable(描述其私有行为)。

@H_502_7@算术运算的元方法 @H_502_7@
Set = {} --对于集合的 *** 作有,取并集和取交集; 对于两个集合的关系有,是否包含,是否相等Set.mt = {} --集合的元表--根据参数列表中的值创建一个新的集合function Set.new(t)    local set = {}    setMetatable(set,Set.mt) --Set.new创建的所有集合都有相同的Metatable    for _,v in pairs(t) do set[v] = true end    return setend--并集 *** 作function Set.union(a,b)         local res = Set.new{} --相当于Set.new({})    for k in pairs(a) do res[k] = true end    for k in pairs(b) do res[k] = true end    return resend--交集 *** 作function Set.intersection(a,b)    local res = Set.new({})    for k in pairs(a) do res[k] = b[k] end    return resend--打印集合的 *** 作function Set.tostring(t)    local tb = {}    for k in pairs(t) do        tb[#tb + 1] = k    end    return "{" .. table.concat(tb,",") .. "}"endfunction Set.print(t)    print(Set.tostring(t))endlocal s1 = Set.new({10,20,30,50})local s2 = Set.new({30,1})print(getMetatable(s1)) -->table: 03B90CD0print(getMetatable(s2)) -->table: 03B90CD0,说明s1与s2有相同的元表Set.mt.__add = Set.union --给Metatable增加__add函数,来去两个集合的全集Set.print(s1 + s2) -->{1,10,50,20}Set.mt.__mul = Set.intersection --给Metatable增加__mul函数,来去两个集合的交集Set.print((s1 + s2)*s1) -->{30,20}

@H_502_7@对于每一个算术运算符,Metatable都有对应的域名与其对应,除了__add(加)、__mul(乘)外,还有__sub(减)、__div(除)、__unm(负)、__pow(幂),我们也可以定义__concat定义连接行为。
当我们对两个表进行加没有问题,但如果两个 *** 作数有不同的Metatable例如:local s3 = s1 + 8 
Lua选择Metamethod的原则:如果第一个参数存在带有__add域的Metatable,Lua使用它作为Metamethod,和第二个参数无关;
否则第二个参数存在带有__add域的Metatable,Lua使用它作为Metamethod 否则报错。
Lua不关心这种混合类型的,如果我们运行上面的s3=s1+8的例子在Set.union发生错误:
bad argument #1 to `pairs' (table expected,got number)

@H_502_7@

function Set.union(a,b)     if getMetatable(a) ~= Set.mt or getMetatable(b) ~= Set.mt then        error("attempt to 'add' a set with a non-set value",2)    end    local res = Set.new{} --相当于Set.new({})    for k in pairs(a) do res[k] = true end    for k in pairs(b) do res[k] = true end    return resend 
@H_502_7@关系运算的元方法

@H_502_7@Metatables也允许我们使用Metamethods:__eq(等于),__lt(小于),和__le(小于等于)给关系运算符赋予特殊的含义。对剩下的三个关系运算符没有专门的Metamethod,因为Lua将a~= b转换为not (a == b);a > b转换为b < a;a >= b转换为 b <= a。

@H_502_7@(直到Lua 4.0为止,所有的比较运算符被转换成一个,a <= b转为not (b < a)。然而这种转换并不一致正确。当我们遇到偏序(partialorder)情况,也就是说,并不是所有的元素都可以正确的被排序情况。例如,在大多数机器上浮点数不能被排序,因为他的值不是一个数字(Not a Number即NaN)。根据IEEE754的标准,NaN表示一个未定义的值,比如0/0的结果。该标准指出任何涉及到NaN比较的结果都应为false。也就是说,NaN <= x总是false,x< NaN也总是false。这样一来,在这种情况下a<= b 转换为 not (b < a)就不再正确了。)

@H_502_7@在我们关于集合 *** 作的例子中,有类似的问题存在。<=代表集合的包含:a <= b表示集合a是集合b的子集。这种意义下,可能a<= b和b < a都是false;因此,我们需要将__le和__lt的实现分开: 

--判断两个集合的包含关系(小于等于)--子集关系Set.mt.__le = function (a,b)    for k in pairs(a) do        if not b[k] then            return false        end    end    return trueend--判断两个集合是否为真子集关系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 <= aendlocal s4 = Set.new({2,4})local s5 = Set.new({4,2})print(s4 <= s5)      --trueprint(s4 < s5)       --trueprint(s4 >= s4)      --trueprint(s4 > s4)       --false print(s4 == s4 * s5) --true

@H_502_7@与算术运算的Metamethods不同,关系元算的Metamethods不支持混合类型运算。@H_502_7@对于混合类型比较运算的处理方法和Lua的公共行为类似。如果你试图比较一个字符串和一个数字,Lua将抛出错误。相似的,如果你试图比较两个带有不同Metamethods的对象,Lua也将抛出错误。

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

总结

以上是内存溢出为你收集整理的Lua元表与元方法全部内容,希望文章能够帮你解决Lua元表与元方法所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1254221.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-07
下一篇 2022-06-07

发表评论

登录后才能评论

评论列表(0条)

保存