*Luaリファレンス 要注意点 ~演算子オーバロード、メタメソッド~ [#k63bd971] **演算子オーバーロード [#k6a9b938] >pythonと同じ方針。メタメソッドに他の一般メソッドの参照を代入する #sh(lua;highlight:[23]){{ 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} }} **演算子種類 [#e06cbb45] >~ | __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 [#e5fceb1d] >''5.3では、ビット演算子や整数除算の演算子が増えてる。'' |__band| & (ビットごとの論理積) 演算。| __add 演算と同様の動作をしますが、メタメソッドが試みられるのはいずれかの引数が整数でなく、整数に変換可能な値でもない場合です。| |__bor| | (ビットごとの論理和) 演算。| __band 演算と同様の動作をします。| |__bxor| ~ (ビットごとの排他的論理和) 演算。| __band 演算と同様の動作をします。| |__bnot| ~ (ビットごとの単項否定) 演算。| __band 演算と同様の動作をします。 | |__shl| << (ビットごとの左シフト) 演算。| __band 演算と同様の動作をします。| |__shr| >> (ビットごとの右シフト) 演算。| __band 演算と同様の動作をします。| |__idiv| // (切り捨て除算) 演算。| __add 演算と同様の動作をします。| **文字列化用のメタメソッド [#s704d0a4] >~ |__tostring| 文字列化する用途。print関数はtostring関数を内部的に呼び出す。| tostringは__tostringメタメソッドが定義されていれば、tostring関数にわたったオブジェクトを引数として__tostringメタメソッドを呼び出す。~ これを前提(呼び出し関係と、引数関係)として__tostringを組む。 **メタメソッドの適応順序 [#j8b9c924] >~ ''a 演算子 b'' という表記を見つけた場合、~ LUAはまず''a が「演算子」のメタメソッドを持っているか''を検索する。~ もっていれば、 そのメタメソッドに割り当てられた関数を実行する。~ ~ もっていなければ、''bが「演算子」のメタメソッドを持っているか''を検索する。~ それもなければ、その''メタメソッドに割り当てられた関数''を実行する。~ ''それすらなければエラー''となるであろう。 **比較メソッド [#c71f95ed] >''比較メソッド(関係演算子メソッド)は型が異なると適応できない。''~ ''算術演算子は型がことなってもどちらかが算術メソッドを持っていれば適応可能。'' **メタメソッドの追加等をロック [#l2e117a1] >対象のオブジェクトに自分で追加等した後(別に追加しなくても良いが)、~ 対象のオブジェクトにメタメソッドの入れ替え(_mtSetの入れ替え)をロックしたい場合、~ ''メタテーブルの__metatableメタメソッドに真を突っ込む。''(通常は文字列を突っ込む)。~ (※_metSetへの差し替え、getmetatableによる読み込みを抑えるだけなので、~ 一度採用してしまった_mtSetがさらにメタテーブルを持つのは抑えられないもよう。~ ある意味中途半端な仕様のようだ。) #sh(lua){{ 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 }} **__index [#b9dc5a62] >テーブルの特定の''値取得時''に、対応するキーが無い場合に呼び出される。 #sh(lua){{ Window = {} --デフォルト値を持つプロトタイプを作成 Window.prototype = { x = 0, y = 0, width = 100, height = 100 } Window.mt = {} -- メタテーブルの作成。 --通常はこのようにメタテーブルを与えたいオブジェクト自信のプロパティとして含めた方が良いだろう。 --コンストラクタ function Window.new (o) setmetatable(o, Window.mt) return o end --indexメタメソッドの定義 Window.mt.__index = function(table, key) return Window.prototype[key] end w = Window.new{x=10, y=30} print(w.height) --> 存在しないので、オブジェクトに帰属するメタテーブルの__indexが呼ばれる。結果Window.prototype["height"]が呼ばれる。 }} **rawget [#r124da84] >__indexを定義したが、__indexを経由せず呼び出したい場合、rawget(t, i)を利用する #sh(lua){{ w = Window.new{x=10, y=30} print(rawget(w, "width")) ---> nil print(rawget(w, "x")) ---> 10 }} **__newindex [#x5aa284f] >>テーブルの特定の''値設定時''に、対応するキーが無い場合に呼び出される。~ こちらは基本的に代入を主目的として呼び出されることとなる。~ ~ この場合、''__newindexを経由しない代入には、rawset(t, k, v)''が対応する。 **デフォルト値を持つテーブル [#lf602986] >~ #sh(lua){{ function setDefault(t, d) local mt = {__index = function() return d end } setmetatable(t, mt) end tab = {x=10, y=20} print(tab.x, tab.z) -->10, nil setDefault(tab, 0) print(tab.x, tab.z) -->10, 0 }} **テーブルを横断して、1つのメタテーブルで済ます。しかし安全に一意な値を使う。 [#r27517d4] >~ #sh(lua){{ local key = {} -- これはkeyとしてアドレスを使用することで一意性を確保する local mt = {__index = function (t) return t[key] end } function setDefault(t, d) t[key] = d setmetatable(t, mt) end -- keyは「table: 000000005FD180」 みたいな値となる(実行する度に変わる) }} **読み取り専用テーブル [#u7211525] >~ #sh(lua){{ function ReadOnly(t) local proxy = {} --> 中間のプロキシテーブル local mt = { --> プロキシテーブルは要素が全部存在しない。これで全てがトレースされる。 __index = t, --> 読み取りはトレースする必要がない。追跡(リダイレクト)する必要がない場合には、テーブル自身を指定できる。 __newindex = function(t, k, v) error("attempt to update a read-only table", 2) end } setmetatable(proxy, mt) return proxy end days = readOnly{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"} }} **メタメソッド __call [#y10bf385] >__call: 値が関数として評価されて呼ばれたときに呼ばれます。 #sh(lua){{ function function_event (func, ...) if type(func) == "function" then return func(...) -- プリミティブの call else local h = metatable(func).__call if h then return h(func, ...) else error(···) end end end }}