Luaリファレンス 要注意点 ~演算子オーバロード、メタメソッド~

演算子オーバーロード

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には存在しないので注意。

演算子種類 Lua 5.3

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

トップ   一覧 単語検索 最終更新   ヘルプ   最終更新のRSS