使用 Eventtable 实现类和方法 |
|
注意:在 Lua 4.1 (work4) 中,metatable() 命令已更改为 metatable() -- Dominik Wagner
注意:我更改了文章以反映此更改。-- JamesHearn
要了解标签方法,首先将 gettable 和 settable 事件设置为 print 函数。这样,您就可以看到在发生表访问时传递给事件函数的参数。
$ lua Lua 4.1 (work4) Copyright (C) 1994-2001 TeCGraf, PUC-Rio > t = { 11,22,33 , one = "a", two = "b", three = "c" } > foreach(t,print) 1 11 2 22 3 33 one a three c two b > mt = { gettable = print, settable = print } > metatable( t, mt ) > x = t[2] table: 0x80631d0 2 > = t table: 0x80631d0 > x = t.three table: 0x80631d0 three > x = t["three"] table: 0x80631d0 three > t.one = 'rat' table: 0x80631d0 one rat > t[1] = 99 table: 0x80631d0 1 99 > = x nil >
settable 和 gettable 事件很简单,但 index 事件更有用。当您尝试从表中获取表中不存在的值时,会调用 index 事件。您可以针对这些情况执行一些特殊操作,例如在另一个表中查找或执行您想要的任何操作。
> foreach(t,print) 1 11 2 22 3 33 one a three c two b > mt = { index = print } > metatable( t, mt ) > x = t.fish table: 0x80631d0 fish > = t table: 0x80631d0 > x = t[44] table: 0x80631d0 44 > x = t['zzz'] table: 0x80631d0 zzz > = x nil >
Lua 4.1 (work4) 允许您将这些标签方法设置为函数或其他表以供访问。
> foreach(t,print) 1 11 2 22 3 33 one a three c two b > u = { cow = 'big' } > mt.index = u > foreach(u,print) cow big > = t.cow big >
以下内容是您在 Perl 中创建类所需的一切。
类的对象只是一个表,其元表设置为另一个表,该表是类。
您可以将所有类方法放在类表中,并将 index 方法设置为类表。请注意,下面的函数 'class' 如何将其 index 成员设置为自身。
然后,当您执行类似 t:fun(4) 的方法调用时,这相当于 t.fun(t,4),如果表 't' 不包含函数 'fun',它将使用 index 方法在类表中查找。
------------------------------------------------------------------------------ -- function class( t ) t = t or {} t.index = t t.new = new t.copy = copy return t end ------------------------------------------------------------------------------ function new( class, init ) init = init or {} return metatable( init, class ) end ------------------------------------------------------------------------------ function classof( x ) return type(x)=='table' and metatable(x) end ------------------------------------------------------------------------------ function copy( obj ) local newobj = classof( obj ):new() for n,v in obj do newobj[n] = v end return newobj end ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ B = class{ name = 'Bob', } function B:who() print(self.name) end function B:hypotenuse() local x,y = self.x, self.y return sqrt( x*x + y*y ) end a = B:new{ x = 4, y = 99, } b = B:new{ x = 5, y = 12, } print( b:hypotenuse() ) ------------------------------------------------------------------------------ ------------------------------------------------------------------------------
$ lua std.lua -i > foreach(B,print) copy function: 0x8066038 index table: 0x8066a08 who function: 0x8065d40 hypotenuse function: 0x80660c0 name Bob new function: 0x80659b8 > foreach(a,print) y 99 x 4 > foreach(b,print) y 12 x 5 > print( b:hypotenuse() ) 13 > = B table: 0x8066a08 >
> > function B:new( x,y ) >> local obj = new( self, { x=x, y=y } ) >> return obj >> end > > c = B:new(1,sqrt(3)) > foreach(c,print) y 1.732050807568877 x 1 > = c:hypotenuse() 2 >
> function B:something(x) >> y = self >> print(x,y) >> debug() >> print(x,y) >> print"finish" >> return x >> end > > b:something(1234) 1234 table: 0x8066548 lua_debug> print(b) table: 0x8066548 lua_debug> y=22 lua_debug> cont 1234 22 finish >
local rawget, metatable, getglobal, gsub = rawget, metatable, getglobal, gsub
事件表是否需要兼任类成员的存储?例如,在 Python 类中,特殊成员使用下划线命名(例如,__index__),以避免与用户字段冲突。当 Lua 设计人员决定在事件表规范中添加一些新字段时,也会出现相同的冲突问题。
不,这只是 Peter 使用的一种便利方式。你可以使用不同的表来存储索引标签方法;这只是意味着需要处理更多的表。Peter 的代码中有一些奇怪的地方(例如,b:new() 被定义了,但它到底做了什么并不完全清楚),这些都是由于将索引表与事件表合并的决定造成的。我个人建议使用不同的表,即使这会使设计稍微复杂一些,原因是出于这个原因,以及 John 关于名称冲突的反对意见。__foo__ 是一种丑陋的解决方法,而不是解决方案:命名空间分离才是解决方案。-- RiciLake
Roberto 最早提出使用元表来存储类的成员方法。我喜欢这种方法,因为这样你就可以说
function some_class:some_method(some_params) does_something end
如果这是推荐的做法,那么系统的事件表字段应该明确命名以避免冲突。
以下代码应该可以解决 b:new() 问题,因为无论 new
传入的是类表还是类对象,obj.index
始终返回类表。
function class( t ) t = t or {} t.index = t t.new = new t.copy = copy return t end function new( obj, init ) init = init or {} return metatable( init, obj.index ) end function copy( obj, init ) local newobj = obj:new(init) for n,v in obj do newobj[n] = v end return newobj end
$ lua -v std2.lua -i Lua 4.1 (work4) Copyright (C) 1994-2001 TeCGraf, PUC-Rio > foreach(B,print) copy function: 0x8066fb0 index table: 0x8067b40 who function: 0x8066cb8 hypotenuse function: 0x8067038 name Bob new function: 0x8066b18 > foreach(b,print) y 12 x 5 > w = b:new() > foreach(w,print) > =B table: 0x8067b40 > =b.index table: 0x8067b40 > =w.index table: 0x8067b40 > z = b:new{ x=1,y=sqrt(3) } > = z:hypotenuse() 2 > foreach(z,print) y 1.732050807568877 x 1 > zz = z:copy() > foreach(zz,print) y 1.732050807568877 x 1 > =z table: 0x8068488 > =zz table: 0x80686d0 > > b:who() Bob > c=b:copy{ z=3 } > foreach(c,print) y 12 x 5 z 3 > c:who() Bob >