*Luaリファレンス 要注意点 ~オブジェクト指向~ [#kee16b7e] **インボカント ~暗黙の第1引数self~ [#ja94c102] -PerlやPythonでは、明記必須のインボカント。Luaにも存在します。~ インボカントこそ、手続き型言語をオブジェクト指向化する中核概念といえるでしょう。 ~ #sh(lua){{ Account = { balance = 0 } function Account.withdraw (v) Account,balance = Account.balance - v end }} これはテーブル内にメソッド的なものを定義した例ですが、これだと #sh(lua;highlight:[2]){{ a = Account Account = nil a.withdraw(10) ---> エラー }} となってしまいます。 -そこで、第1引数とインボカントが活躍します。 #sh(lua){{ function Account.withdraw ( self, v ) self.balance = self.balance - v end }} -多くの言語では、1番目のself(またはthisとなっている言語も多い)の引数は見えなくなっています。 #sh(lua;highlight:[1]){{ function Account:withdraw( v ) ---> 「.」ではなく、「:」を使うと、1番目の引数にselfを書いたことと同じこととみなされる。 self.balance = self.balance - v end }} -呼び出しも「:」を使えば第1引数にオブジェクト自体を代入したことと同じとなる。~ 通常記法も使えるがその場合はやや間が抜けた記述となるだろう。~ #sh(lua;highlight:[4]){{ a = Account Account = nil a.withdraw(a, 10) --> 通常記法。第1引数にテーブル(オブジェクト)自身を渡す。 a:withdraw(10) --> シンタックスシュガー。第1引数にオブジェクト自身を渡していることになる。 }} **インボカントとインスタンス化 [#zbefa9c6] -インボカント #sh(lua;){{ Account = { balance = 0 } function Account:new (o) --「:」に注意 o = o or { } setmetatable(o, self) self.__index = self return o end function Account:deposit (v) self.balance = self.balance + v end a = Account:new{ balance = 0 } a:deposit(100) }} としたとすると、 #sh(lua;){{ Account:new { balance = 0 } }} などといったように使用すると、メタテーブルの__indexに対して~ #sh(lua;){{ Account.__index = Account }} となる。~ ~ そして、a:deposit(100)は a.deposit(a, 100)となるので、~ aに存在しないdepositメソッドは~ getmetatable(a).__index.deposit(a, 100) と検索される。~ ~ 結果、''Account.deposit(a, 100)'' と指定したことと同じことになる。~ ~ 即ち、aは自身にメソッドが存在しない時、Account(を一種親的にみなし)呼び出す、 一種、オブジェクト指向の「継承」や「デフォルト値」があるかのような機構を構築出来るということである。 -フィールドの初期値の継承 又、''フィールドの初期値も継承''する。~ 例えば、~ #sh(lua;){{ b = Account:new() print(b.balance) -- b.balance は b自体には存在しないので、Account.balance を見ることとなる。 }} 一方、これにたいして、 #sh(lua;){{ b:deposit(10) }} などと呼び出した場合は、Account.deposit(b, 10)が呼び出される形となり、b.blanace = b.balance + 10 されるので、~ この段階で、b自身にbalanceフィールドが形成されることとなる。~ -クラスの継承 #sh(lua;){{ Account = { balance = 0 } function Account:new (o) o = o or { } setmetatable(o, self) self.__index = self return o end function Account:deposit(v) self.balance = self.balance + v end function Account:withdraw(v) if v > self.balance then error ("んな金ねーぉ") end self.balance = self.balance - v end }} ここで、このAccountを継承するSpecialAccountクラスを作成する #sh(lua;){{ SpecialAccount = Account:new() -- この段階ではまだインスタンス s = SpecialAccount:new{limit = 1000.00} -- new を実行した時、selfのパラメータがSpecialAccountとなり、self.__indexもSpecialAccountとなった。 }} これにより、sのメタテーブルはSpecialAccountとなり、sはSpecialAccountのインスタンスであるかのように振舞える。~ ~ #sh(lua;){{ s:deposit(100) }} を呼び出すと、~ ''getmetatable(s).__index.deposit(s, 100)となるので、''~ ''ScecialAccount.deposit(s, 100)を実行しようとするが、このdepositも存在しないため、''~ ''getmetatable(SpecialAccount).__index.deposit(s, 100)となり、''~ ''結果、''~ ''Account.deposit(s, 100)が実行される。''~ ~ ''即ち、インスタンス、子クラス、親クラスの、メソッド探査(階層チェーンの伝播)が構築できたこととなるのである。''