Lua元表の実装と応用

Luaにおける元表(metatable)は、テーブルの振る舞いをカスタマイズするための仕組みです。通常のテーブルでは定義されていない演算やアクセス方法を、元表を通じて再定義することで、より柔軟なデータ構造を実現できます。

元表の基本設定

元表はsetmetatable関数を用いてテーブルにアタッチします。元表自体も通常のテーブルであり、特定のキー(メタメソッド)を持つことで振る舞いを制御します。

local base_table = {}
local meta = {}
local instance = setmetatable(base_table, meta)

元表の保護:_metatableの利用

元表の外部からのアクセスを制限したい場合、元表内に_metatableキーを設定することで、getmetatableの返値を固定できます。このとき、元表そのものは隠蔽され、外部から変更できなくなります。

local protected = { _metatable = "hidden" }
local obj = setmetatable({}, protected)
print(getmetatable(obj)) -- "hidden" が返る
-- 元表の内容は直接参照・変更不可

__index:キー未定義時のフォールバック処理

__indexメタメソッドは、テーブルのキーが存在しない場合に呼び出されます。値として関数またはテーブルを指定できます。

関数による動的処理

local data = { "Lua", "Java", "C#" }
local fallback = {
  __index = function(tbl, key)
    print("アクセスされたキー: " .. tostring(key))
    return "JavaScript"
  end
}
data = setmetatable(data, fallback)

print(data[99]) -- 出力: アクセスされたキー: 99\nJavaScript

テーブルによる継承

local parent = { x = 100, y = 200 }
local child = { name = "point" }
local meta = { __index = parent }
child = setmetatable(child, meta)

print(child.x) -- 100(parentから継承)
print(child.name) -- "point"(child自身の値)

__newindex:新規代入のフック

__newindexは、存在しないキーへの代入時にのみ発火します。既存キーの更新には影響しません。これにより、ログ記録や値のバリデーションが可能です。

local storage = {}
local proxy = {
  __newindex = function(tbl, key, value)
    if type(value) ~= "string" then
      error("文字列のみ許可されます")
    end
    print("新規設定: " .. key .. " → " .. value)
    rawset(tbl, key, value) -- 実際の格納
  end
}
storage = setmetatable(storage, proxy)

storage.id = "user_001" -- 出力: 新規設定: id → user_001
storage.id = "user_002" -- 何も出力されない(既存キー)

演算子のオーバーロード

元表では算術・比較演算子を再定義できます。以下は代表的なメタメソッドです。

  • __add+
  • __sub-
  • __mul*
  • __div/
  • __eq==
  • __lt<
  • __le<=

例:二つのリストを連結する

local list1 = { "A", "B" }
local list2 = { "C", "D" }

local list_meta = {
  __add = function(a, b)
    local result = {}
    for _, v in ipairs(a) do table.insert(result, v) end
    for _, v in ipairs(b) do table.insert(result, v) end
    return result
  end
}

list1 = setmetatable(list1, list_meta)
local combined = list1 + list2
print(table.concat(combined, ",")) -- A,B,C,D

__call:テーブルの関数呼び出し

テーブルを関数のように呼び出すと、__callが実行されます。これにより、ファクトリーオブジェクトやコールバックオブジェクトを構築できます。

local counter = { value = 0 }
local meta = {
  __call = function(tbl, increment)
    tbl.value = tbl.value + (increment or 1)
    return tbl.value
  end
}
counter = setmetatable(counter, meta)

print(counter())      -- 1
print(counter(5))     -- 6
print(counter())      -- 7

__tostring:文字列表現のカスタマイズ

print()や文字列変換時に__tostringが呼び出され、テーブルの表示形式を制御できます。

local config = { host = "localhost", port = 8080 }
local meta = {
  __tostring = function(tbl)
    return "Config[host=" .. tbl.host .. ", port=" .. tbl.port .. "]"
  end
}
config = setmetatable(config, meta)

print(config) -- Config[host=localhost, port=8080]

タグ: lua 元表 メタメソッド __index __newindex

7月31日 08:20 投稿