この第1版はLua 5.0向けに執筆されました。後続のバージョンでも大部分は関連性がありますが、いくつかの相違があります。
第4版はLua 5.3を対象にしています。Amazonおよび他の書店で購入できます。
書籍を購入することで、Luaプロジェクトのサポートにもつながります。


13.1 – 算術メタメソッド

このセクションでは、メタテーブルの使い方を説明するためにシンプルな例を紹介します。集合を表すのにテーブルを使用し、2つの集合の和、交差などの計算を行う関数を想定します。リストの場合と同様に、これらの関数をテーブル内に格納し、新しい集合を作成するコンストラクターを定義します

    Set = {}
    
    function Set.new (t)
      local set = {}
      for _, l in ipairs(t) do set[l] = 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
    
    function Set.intersection (a,b)
      local res = Set.new{}
      for k in pairs(a) do
        res[k] = b[k]
      end
      return res
    end
例を確認するために、集合を出力する関数も定義します
    function Set.tostring (set)
      local s = "{"
      local sep = ""
      for e in pairs(set) do
        s = s .. sep .. e
        sep = ", "
      end
      return s .. "}"
    end
    
    function Set.print (s)
      print(Set.tostring(s))
    end

さて、加算演算子 (`+´) を使用して2つの集合の和を計算できるようにしたいとします。そのためには、集合を表すすべてのテーブルがメタテーブルを共有し、このメタテーブルが加算演算子への反応方法を定義します。最初の手順として、集合のメタテーブルとして使用する通常のテーブルを作成します。名前空間が汚れないように、Setテーブルに格納します

    Set.mt = {}    -- metatable for sets
次の手順として、集合を作成するSet.new関数を変更します。新しいバージョンには追加の行が1つだけあり、作成するテーブルのメタテーブルとしてmtを設定します
    function Set.new (t)   -- 2nd version
      local set = {}
      setmetatable(set, Set.mt)
      for _, l in ipairs(t) do set[l] = true end
      return set
    end
これ以降、Set.newを使用して作成するすべての集合には、そのメタテーブルとして同じテーブルが設定されます
    s1 = Set.new{10, 20, 30, 50}
    s2 = Set.new{30, 1}
    print(getmetatable(s1))          --> table: 00672B60
    print(getmetatable(s2))          --> table: 00672B60

最後に、メタテーブルにいわゆるメタメソッドを追加します。これは、和の計算方法を表すフィールド__addです

    Set.mt.__add = Set.union
Luaが2つの集合を加算しようとするたびに、この関数が引数として2つのオペランドを使用して呼び出されます。

メタメソッドが設定されたら、加算演算子を使用して集合の和を求めることができます

    s3 = s1 + s2
    Set.print(s3)  --> {1, 10, 20, 30, 50}
同様に、乗算演算子を使用して集合の交差を求めることができます
    Set.mt.__mul = Set.intersection
    
    Set.print((s1 + s2)*s1)     --> {10, 20, 30, 50}

各算術演算子には、メタテーブル内の対応するフィールド名があります。__add__mulのほかに、__sub(減算)、__div(除算)、__unm(否定)、および__pow(べき乗)があります。連結演算子の動作を定義するフィールド__concatを定義することもできます。

2つの集合を加算するときは、使用するメタテーブルに疑問はありません。ただし、2つの異なるメタテーブルを持つ2つの値を混在させる式を記述する場合があります。例えば、次のようにです

    s = Set.new{1,2,3}
    s = s + 8
メタメソッドを選択するために、Luaは次のようにします。(1) 最初の値のメタテーブルに__addフィールドがある場合、Luaはこの値をメタメソッドとして使用し、2番目の値には依存しません。(2) そうでない場合、2番目の値のメタテーブルに__addフィールドがある場合、Luaはこの値をメタメソッドとして使用します。(3) そうでない場合、Luaはエラーを発生させます。したがって、最後の例ではSet.unionが呼び出され、式10 + s"hy" + sでも同様に呼び出されます。

Luaはこれらの混在型を気にしませんが、実装は気になります。s = s + 8の例を実行すると、Set.union内でエラーが発生します

    bad argument #1 to `pairs' (table expected, got number)
よりわかりやすいエラーメッセージを表示したい場合は、操作を実行する前にオペランドの型を明示的にチェックする必要があります
    function Set.union (a,b)
      if getmetatable(a) ~= Set.mt or
         getmetatable(b) ~= Set.mt then
        error("attempt to `add' a set with a non-set value", 2)
      end
      ...  -- same as before