pythonと同じ方針。メタメソッドに他の一般メソッドの参照を代入する
Set = {} local _mtSet = {} --> メタテーブル用 function Set.new (l) local set = {} setmetatable(set, _mtSet) --> テーブルに対してメタテーブルが用意されるようにする。 for _, v in ipairs(l) do set[v] = 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 s1 = Set.new{10, 20, 30, 50} s2 = Set.new{30, 1, 60} print(getmetatable(s1)) --> table : 00000000xxxxx1 print(getmetatable(s2)) --> table : 00000000xxxxx2 _mtSet.__add = Set.union --> メタテーブル __add に代入する(ことで、「+」の振る舞いを変える s3 = s1 + s2 --->s3 = Set.union(s1, s2) --> {1,10,20,30,50, 60}
__add + 演算。 __sub - 演算。 __add演算と同じように動作する。 __mul * 演算。 __add演算と同じように動作する。 __div / 演算。 __add演算と同じように動作する。 __mod % 演算。 __add演算と同じように動作する。 __pow ^ (累乗) 演算。 __add演算と同じように動作する。プリミティブ演算として関数 pow (Cの数学ライブラリ) を使う。 __unm 単項 - 演算。 __concat .. (連結) 演算。 __len # 演算。 __eq == 演算。 a ~= b は not (a == b) と等価である。 (よって __neqは無い) __lt < 演算。 a > b は b < a と等価である. (よって __geは無い) __le <= 演算。 a >= b は b <= a と等価である。(よって __gtは無い) __leメタメソッドがなければ、 Luaは a <= b を not (b < a) とみなして__ltを試みることに注意。
5.2までは & や| は無い。
5.2まではビット演算やシフト演算の類は、元々LUAには存在しないので注意。
5.3では、ビット演算子や整数除算の演算子が増えてる。
__band & (ビットごとの論理積) 演算。 __add 演算と同様の動作をしますが、メタメソッドが試みられるのはいずれかの引数が整数でなく、整数に変換可能な値でもない場合です。 __bor | (ビットごとの論理和) 演算。 __band 演算と同様の動作をします。 __bxor ~ (ビットごとの排他的論理和) 演算。 __band 演算と同様の動作をします。 __bnot ~ (ビットごとの単項否定) 演算。 __band 演算と同様の動作をします。 __shl << (ビットごとの左シフト) 演算。 __band 演算と同様の動作をします。 __shr >> (ビットごとの右シフト) 演算。 __band 演算と同様の動作をします。 __idiv // (切り捨て除算) 演算。 __add 演算と同様の動作をします。
__tostring 文字列化する用途。print関数はtostring関数を内部的に呼び出す。 tostringは__tostringメタメソッドが定義されていれば、tostring関数にわたったオブジェクトを引数として__tostringメタメソッドを呼び出す。
これを前提(呼び出し関係と、引数関係)として__tostringを組む。
a 演算子 b という表記を見つけた場合、
LUAはまずa が「演算子」のメタメソッドを持っているかを検索する。
もっていれば、 そのメタメソッドに割り当てられた関数を実行する。
もっていなければ、bが「演算子」のメタメソッドを持っているかを検索する。
それもなければ、そのメタメソッドに割り当てられた関数を実行する。
それすらなければエラーとなるであろう。
比較メソッド(関係演算子メソッド)は型が異なると適応できない。
算術演算子は型がことなってもどちらかが算術メソッドを持っていれば適応可能。
対象のオブジェクトに自分で追加等した後(別に追加しなくても良いが)、
対象のオブジェクトにメタメソッドの入れ替え(_mtSetの入れ替え)をロックしたい場合、
メタテーブルの__metatableメタメソッドに真を突っ込む。(通常は文字列を突っ込む)。
(※_metSetへの差し替え、getmetatableによる読み込みを抑えるだけなので、
一度採用してしまった_mtSetがさらにメタテーブルを持つのは抑えられないもよう。
ある意味中途半端な仕様のようだ。)
Set = {} local _mtSet = {} --> メタテーブル用 function Set.new (l) local set = {} setmetatable(set, _mtSet) for _, v in ipairs(l) do set[v] = 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 s1 = Set.new{10, 20, 30, 50} s2 = Set.new{30, 1, 60} print(getmetatable(s1)) --> table : 00000000xxxxx1 print(getmetatable(s2)) --> table : 00000000xxxxx2 _mtSet.__add = Set.union _mtSet.__metatable = "not your business" --メタテーブルをロック。真の値を入れさえすればよい。 print(getmetatable(s1)) --> not your business _mtSet._mul = Set.union setmetatable(s1, {}) --> cannot change protected metatable