Lua 原表(metatable )

Metatables允许我们改变table的行为,例如,使用Metatables我们可以定义Lua如何计算两个table的相加操作a+b。当Lua试图对两个表进行相加时,他会检查两个表是否有一个表有Metatable,并且检查Metatable是否有__add域。如果找到则调用这个__add函数(所谓的Metamethod)去计算结果。

Lua中的每一个表都有其Metatable。Lua默认创建一个不带metatable的新表


如果类比C++,原表有些类似于一个关于表操作的基类+operate操作。其他的表都是继承与这个基类。可以在这个基类中定义关于表的操作。不同之处在于,C++中的继承关系需要在声明中确定,但是Lua中的这种类似的继承关系不需要声明的时候就确定,而是在声明之后再确定的。


一、自定义原表的方法

例如,《Lua in Programming》中的关于两个表运算,即求并如下:

</pre><pre name="code" class="plain">Set={};
Set.mt={};

--mt={};

function Set.new (t)
	local set={};
	setmetatable(set,Set.mt);
	--setmetatable(set,mt);
	for _,l in ipairs(t) do set[l] = true end
	return set
end

--并集
function Set.union(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

--交集
function Set.intersection(a,b)
	local res=Set.new{};
	for k in pairs(a) do
		res[k]=b[k];
	end

	return res;
end


function Set.tostring(set)
	local s="{";
	local sep="";
	for e in pairs(set) do
		s=s .. sep .. e;
		sep=",";
	end

	return s .. "}"
end


function Set.print(s)
	print(Set.tostring(s));
end

Set.mt.__add=Set.union;
--mt.__add=Set.union;

Set.mt.__mul=Set.intersection;
--mt.__mul=Set.intersection;

s1=Set.new{10,20,30,50};
s2=Set.new{30,1};

print(getmetatable(s1));
print(getmetatable(s2));


s3=s1+s2;
Set.print(s3);

s4=s1*s2;
Set.print(s4)
输出:

table: 0080B488
table: 0080B488
{1,30,10,50,20}
{30}

注意:

1、用注释替换其上一行代码,结果一样

2、__add,__mul都是两个下划线


两个表关系运算,接上例:

<pre name="code" class="plain">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

s5=Set.new{2,4}
s6=Set.new{4,10,2}

print(s5<=s6)
print(s5<s6)
print(s5>=s5)
print(s5>s6)
print(s5==s5*s6)


 输出: 

true
true
true
false
true


问题:在SciTE下,有时候需要复制s5,s6在粘贴。好像是输入法影响的问题。


二、原表本身的方法

1、__index metamethod

即索引。对任何一个表通过索引取值的时候,其实都是通过__index metamethod去寻址取值的。当然可以更改索引方法的取值,类似于C++中的operate[]。代码如下:

window={};

window.protype={x=1,y=0,width=100,height=200,}


window.mt={};

function window.new(o)
	setmetatable(o,window.mt)
	return o;
end

window.mt.__index=function(table,key)
	return window.protype[key]
end
--window.mt.__index=window.protype;

w=window.new{x=10,y=20};
print(w.x)
print(w.y)
print(w.width)
print(w.height)
结果:

10
20
100

200

<span style="font-family: KaiTi_GB2312; background-color: rgb(255, 255, 255);">实质上就是重写了[]的作用。用指数替换上一个函数结果相同。</span>


2、为边添加默认值:使用索引值获取表的内容且索引值不存在的时候,返回nil。如果想要返回特定值,方法如下:

function  setdefault(t,d)
	local mt={__index=function() return d;end}
	setmetatable(t,mt);
end

t={x=10,y=20}
print(t.x,t.y,t.z);

setdefault(t,0);
print(t.x,t.y,t.z);
结果:

10 20 nil
10 20 0

3、监控表:就是监控表的__index和__newindex两个操作

t={};

local _t=t;

t={};

local mt={
	__index=function(t,k)
	print("access to element"..tostring(k));
	return _t[k];
	end;

	__newindex=function(t,k,v)
	print("update of element"..tostring(k).."to"..tostring(v));
	_t[k]=v;
	end;
}

setmetatable(t,mt);

t[2]="hello"
print(t[2])
结果:

update of element2tohello
access to element2
hello

简而言之就是监督所有的所以的索引操作,通过索引取值和新建一个索引。


3、只读表:通过以上两个例子可以容易想到,所谓的只读表就是将__index和__newindex的功能改变:

function readonly(t)
	local proxy={};
	local mt={
	__index=t;
	__newindex=function(t,k,v)
		print("error",2)
	end
	}

	setmetatable(proxy,mt);
	return proxy;
end

data=readonly{"1","2","3"};

print(data[1]);
data[2]=3;
结果:

1
error 2


与C++类比,有点像一个对象只能在栈上生成的时候,就是将new设为私有。


Lua有很多可以和C++类比的地方,可以通过类比理解。





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值