使用事件表建立類別和方法 |
|
注意:在 Lua4.1 (work4) 中,metatable() 指令已變更為 metatable() -- Dominik Wagner
注意:我已更改文章以反映此變更。-- JamesHearn
若要了解標籤方法,請先將建取事件和設定事件設定為 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 >
設定事件和建取事件很直接,但索引事件更有用。當您嘗試從表格中取得表格沒有的值時,就會呼叫索引事件。對於這些情況,您可以做點特別的事,例如在另一個表格或您想要的任何位置查看。
> 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 中製作一樣。
類別的物件只是一個表格,其 meta-table 設定為另一個表的類別。
您可以將所有類別方法放入類別表格並將索引方法設定為類別表格。請注意以下我的「類別」函數是如何將其索引成員設定為它本身的。
然後,當您執行類別呼叫,例如 t:fun(4),這是 t.fun(t,4) 的簡寫,如果表格「t」沒有函數「fun」,它將使用索引方法在類別表格中查看。
------------------------------------------------------------------------------ -- 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() 問題,因為 obj.index
隨時提供類別表,無論 new
傳入的是類別表或類別的物件。
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 >