欢迎光临散文网 会员登陆 & 注册

Lua 元表与元方法

2021-02-03 22:42 作者:多华宫与火火里  | 我要投稿

一点点讲,娓娓道来。

因为不那么好理解,至少不像1+1=2那样容易理解。

比如现在建立一个表xx={23}然后显然这个表里只有一个元素,那么如果你想要xx[2]这个东西,你就会发现这东西当然不存在,即为nil,你只能用xx[1]这个表中唯一的东西,还有当然这个xx[1]等于23

然后呢,lua中有个东西名叫元表,元表是什么呢,首先元表是个表,其次这个表能改变其它别的表的“行为”。好像很奇怪,举个例子,比如有两个表,一个xx={23}一个oo={66},这两个表各是各的,你当然也没办法说把两个表乘起来或者加起来,或者也没办法使用一个你原来表里就没有的元素,而现在,为了对两个table进行操作,lua中为你准备了“元表”这么一个东西

再来刚刚的,我们建立一个表xx={23},然后我们再建立一个表,当然表名字随便,比如建个表kkm40={},然后呢,我们可以设置这两个表的关系,让它们有点关系,我们设置kkm40为xx的元表,设置元表首先一定要用到setmetatable函数,这个函数是lua里的函数(就像lua里有math.random函数一样),这个setmetatable函数一共有两个参数,你要填两个表,它的作用就是,设定后一个表是前一个表的元表,比如你现在setmetatable(xx,kkm40)的话就表示你设定xx这个表的元表是kkm40,那么具体啥是元表呢,你可以把“元表”理解为一个“背后的支持者”,没错,kkm40就是xx背后的支持者!那么它要怎么支持呢?比如像刚刚你想用xx[2]的时候,诶,没有这个东西,那此时你背后的支持者——元表kkm40就可以帮助你,为你提供比如xx[2]这个东西,但是先莫要慌,因为现在kkm40这个表还是个空表,对吧,刚刚建立的是kkm40={},空的肯定没啥用,你要成为背后的支持者,那么还需要为kkm40这个“元表”里面设定一些“元方法”,这些元方法就可以来支持xx表了。那就来设置元方法吧,在kkm40表里再设定一个表,像这样

kkm40={__index={[2]=666}}

这样就设定了一个元方法,那么此时你用setmetatable(xx,kkm40)的话,就和刚刚当然不一样了,然后这样设置好元表以后,你就可以用print打印出xx[2]了,你就能得到666而不会像一开始那样得到nil,文字描述了这些以后,当然目前所形容的代码如下

xx={23}

kkm40={__index={[2]=666}}

setmetatable(xx,kkm40)

print(xx[2])

打印结果当然是666了

然后,继续解释,首先是,这个设置元方法啊,那个“语法”是固定的,比如我刚刚用到的__index这个东西不是我随便取名字取的,如果是固定语法我就会特意强调的,比如我说一个表的名字可以随便取,就像我取名kkm40一样,但是也有不能自由发挥的地方,那就是固定语法,比如for就是固定语法,你当然不能用fffor来实现for循环了,对吧,这里的__index也一样是固定语法,需要记清楚,这个__index中有两个下划线,不是_而是__      

再然后,index也是死的,不是我随便取名的,所以这个__index就和for一样是死的,不要用fooooor来循环哦!

然后,现在不是能print出xx[2]了吗,需要注意的是,你设置元表,至少是没有改变你原来的表的,也就是说你的xx这个表其实还是原来的{23}这个样子,而不是{23,666},那么为什么现在却能print出xx[2]呢,因为在你设置好元表以后,如果你要访问表里的某个东西,那么首先会在你原来的表里查找,看你原来的表里有没有这个东西,如果没有就会到你这个表的元表里查找有没有这个东西,如果有的话,你当然就能访问到,如果元表里都还没有的话,就会访问不到了,你就访问的是nil了。所以刚刚的过程是这样的:

建一个xx={23}然后建立表kkm40={__index={[2]=666}},用setmetatable函数设定kkm40为xx的元表,然后我们print打印xx[2],那就开始找,在xx表里发现没有xx[2],因为xx是等于{23}这个的,那么开始往元表kkm40里看,看kkm40里有没有设定元方法,发现诶有元方法,看看里面能不能找到相应的键,发现里面有[2]这个东西,所以kkm40这个背后的支持者就让你成功的打印出了xx[2],但是这不代表你xx这个原来的表有任何改变,你xx还是等于{23}的。所以需要记住,在设置了元表以后,如果元表里有__index这个元方法的话,那么当你访问原来的table里的元素的时候,如果里面找不到相应的键,就会到你元表中的这个__index元方法里面查找有没有,有就可以访问到。(注意一点啊,你这么设定了元表以后,如果原table里找不到它就一定会在元表里面找一找有没有,如果你设定了元表又不想它到元表里找一找的话,就涉及到一些其它的知识点了,这先不讲太多,并不是本次所讲的重点。只需记住,你像这样设定了元表,那么你原来table里没有这个东西的话,它就一定一定一定会再跑到你的元表里找一找、看一看有没有)

所以根据以上所讲,显然在你设置了元表的时候,如果你在原来表里能访问到某元素的话,它就不会到元表里找了,只有在访问不到的时候才会坚决地去元表里面找一找,所以如果是比如如下这种代码的话,它就会print出23而不是666

xx={23}

kkm40={__index={[1]=666}}

setmetatable(xx,kkm40)

print(xx[1])

原因很简单,就是你原来的xx表里有xx[1]所以就直接访问了,它就不会去看你的元表,所以结果不会是你元表中设定的[1]=666,而是xx[1]就等于23了

然后,另外,刚刚这个__index设定的它是一个表嘛,设的是__index={[2]=666}

但是你其实还可以设定成函数,比如你设定__index=function(tbl,aaaaa) if aaaaa==2 then return 666 end end  这样和刚刚的效果是一样的,比如你设定了kkm40为xx的元表,那么你在这元方法里用的这函数的参数会自动给你传入,第一个参数就自动是你原来的表,即xx,第二个参数就自动指的相应的“键”,参数名字当然都是随便的,比如这里取的tbl和aaaaa,所以像是如下的代码,就和前面提到的另一种写法是等价的

xx={23}

kkm40={

__index=function(tbl,kkk)

if kkk==2 then

return 666

end

end

}

setmetatable(xx,kkm40)

print(xx[2])

一样会print出xx[2]为666

所以同理比如,以下代码

xx={23}

kkm40={

__index=function(tbl,kkk)

if kkk=="ccc" then

return 666

end

end

}

setmetatable(xx,kkm40)

print(xx.ccc)

也是同样道理,xx表里原来没有“ccc”这个下标,那么就在元表里找找看,在元表里有__index这个元方法,那么好,看看里面有没有"ccc"这个下标,发现有,所以成功print出了xx.ccc

好了,刚刚费了这么大劲讲了__index这个固定的元方法,那然后呢,实际上还有更多的元方法供你使用,当然同样的这些元方法都是固定的,就像__index的固定语法一样!!

下面介绍__add这个元方法,这个元方法呢,可以让你自定义两个table的“加法”运算,比如本来你有两个表,一个xx一个oo,这两个表之间当然没办法相加了,但是在有了__add以后,你就可以用出xx+oo这种东西了。

那么来更详细地举例,首先建立一个表xx={23,5,7}然后还建一个表oo={66,-1,2}

现在希望定义运算xx+oo,那么就要用到元表,所以显然我们还需要建立一个表,并将这个表作为xx或oo其中一个的元表,然后还要用到固定语法__add才能使用+加法运算,所以我们再建一个表kkm40={},当然这里面应该有元方法__add,因为我们这里要定义加法运算到底是哪种规则的,所以我们要自定义一个函数,所以我们设kkm40这个表为下面这样

kkm40={

__add=function(tbl,tbl2)

for i=1,#tbl do

tbl[i]=tbl[i]+tbl2[i]

end

return tbl

end

}

看不太舒服的话,那就如图,看如下图片

这样我们就设定了__add元方法,我们设定一个自定义函数,函数两个参数名字当然随便取,然后这里同样的,在后面设定好元表以后,用到该元方法时,会自动给函数提供那两个参数,比如你要运算xx+oo那么函数参数就自动填入的是:第一个填xx第二个填oo,反过来的话,如果你要运算oo+xx的话,那么函数自动第一个参数就是oo第二个参数就是xx,好,那你看这个函数怎么定义的呢,for循环遍历tbl表,然后让tbl[i]等于tbl[i]加上tbl2[i],然后最后返回tbl(不要忘记返回tbl哟!),这样的话,我们就规定好这个元方法了,显然这个元方法的规则就是让两个table的元素都一一对着加起来,这样就是+加法运算了,当然你也可以设定不同的规则了,反正这个自定义函数你可以自由发挥嘛。

然后再建立好这个kkm40表以后,当然为了设置元表,我们用上setmetatable函数,就这样setmetatable(xx,kkm40)就是给xx设定一个元表kkm40

在设置好元表以后,当然,你就可以用比如xx+oo这种两个表之间的“加法”运算了,总的来说代码如下

xx={23,5,7}   oo={66,-1,2}

kkm40={

__add=function(tbl,tbl2)

for i=1,#tbl do

tbl[i]=tbl[i]+tbl2[i]

end

return tbl

end

}

setmetatable(xx,kkm40)

abc=xx+oo

for i=1,#abc do

print(abc[i])

end


这样看不太清楚的话,可以看图,图片如下

这样的话,abc就等于xx这个表加oo这个表了,而由于设定了元表和元方法__add,所以+加法运算才能成功执行,那么显然现在abc就会等于{89,4,9}这么一个表了,里面的数字也就是xx的每个元素分别和oo的每个元素加起来了

所以说,如果你想用+对两个table运算,那么就一定要用元表和__add元方法。

那么刚刚说了,自定义函数你可以随便设定,所以你也可以设定成别的"规则",比如你建的kkm40这个表是下面这样的

kkm40={

__add=function(tbl,tbl2)

for i=1,#tbl do

table.insert(tbl,#tbl+1,tbl2[i])

end

return tbl

end

}

这样看不清楚的话,可以看如下图片

在tbl表最后面的位置一个个的插入tbl2的元素

这样的话,总共的代码就是如下这样

那么现在这个abc等于xx+oo这个运算以后,显然abc就和刚刚不一样了,现在元方法里的自定义函数和刚刚不同了,现在的操作是在第一个table的最后的位置一个个插入另一个table的元素,所以现在得到的abc结果就会是{23,5,7,66,-1,2}了,不再是刚刚的{89,4,9}了,就是因为这自定义的+“加法”规则改变了,你也可以定义别的各种各样的规则。

好,我们现在讲了__add元方法,知道了你如果要用两个表来+的话,就必须用元表和__add元方法,那么实际上,除了之前讲的__index和__add元方法以外,还有别的元方法供你使用,当然也都是固定语法了。下面,快速列举一些和__add使用方法相同的元方法

__sub对应的运算符 -

也就是你可以像刚刚用xx+oo那样,你也可以自定义xx-oo是什么规则

__mul对应的运算符 *

也就是你可以像刚刚用xx+oo那样,你也可以自定义xx*oo是什么规

__div对应的运算符 /

也就是你可以像刚刚用xx+oo那样,你也可以自定义xx/oo是什么规则

__pow对应的运算符 ^

也就是你可以像刚刚用xx+oo那样,你也可以自定义xx^oo是什么规则

__mod对应的运算符 %

也就是你可以像刚刚用xx+oo那样,你也可以自定义xx%oo是什么规则

__concat对应的运算符 ..

也就是你可以像刚刚用xx+oo那样,你也可以自定义xx..oo是什么规则









Lua 元表与元方法的评论 (共 条)

分享到微博请遵守国家法律