使用 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
>