Menu
CoddyTech

__newindex メタメソッド

CoddyのLuaジャーニー「ロジックとプログラムの流れ」セクションの一部。レッスン 19/54。

テーブルに存在しないキーを読み取ろうとしたときに何が起こるかを、__indexが制御する仕組みを学びました。次はその対となる__newindexについて見ていきましょう。これは、テーブルにまだ存在しないキーに値を代入したときに何が起こるかを制御します。

__newindexメタメソッドは、テーブルに現在存在しないキーに値を代入しようとするたびに呼び出されます。これにより、変更が行われる前にそれを捕捉して制御できます。

テーブルに新しいキーが追加されないようにする簡単な例を見てみましょう。

local readOnly = {}
local meta = {
    __newindex = function(table, key, value)
        print("Cannot add key: " .. key)
    end
}

setmetatable(readOnly, meta)

readOnly.name = "Alice"  -- 出力: Cannot add key: name
print(readOnly.name)     -- 出力: nil

readOnly.name = "Alice"を設定しようとすると、Luaはテーブルにnameが存在しないことを認識します。Luaはそれを追加する代わりに、3つの引数(テーブル自身、設定するキー、代入する値)を指定して__newindex関数を呼び出します。

この例では、関数は単にメッセージを出力するだけで、実際には何も保存しません。

テーブルにデータを格納する前に検証するために、__newindexを使うこともできます。

local player = {}
local meta = {
    __newindex = function(t, key, value)
        if key == "health" and type(value) ~= "number" then
            print("Health must be a number!")
        else
            rawset(t, key, value)
        end
    end
}

setmetatable(player, meta)

player.health = "high"  -- 出力: Health must be a number!
player.health = 100     -- 許可され、rawset で保存される
player.health = "high"  -- メッセージなし: health はすでに存在する

2つ目の例でrawset()が使われていることに注目してください。この関数はメタテーブルを迂回し、テーブル内の値を直接設定します。

これがないと、値を設定する際に再び__newindexが呼び出され、無限ループが発生します。

__newindex はテーブルに存在しないキーに対してのみ実行されるため、このチェックは各キーへの最初の代入を保護します。rawset で health が保存されると、その後の代入では値が直接変更されます。すべての代入をチェックするには、テーブル自体を空のままにし、__index を通じて読み取る別のテーブルに値を保存します。

local data = {}
local player = setmetatable({}, {
    __index = data,
    __newindex = function(t, key, value)
        if key == "health" and type(value) ~= "number" then
            print("Health must be a number!")
        else
            data[key] = value
        end
    end
})

player.health = 100
player.health = "high"  -- 出力: Health must be a number!
print(player.health)    -- 出力: 100

player自身のキーを持つことはないため、すべての代入は__newindexを経由し、すべての読み取りは__indexを通じてdataにフォールスルーします。

challenge icon

チャレンジ

簡単

新しいキーに対して文字列値のみを受け入れるテーブルを返す関数 createValidatedTable を記述してください。この関数は key と value を受け取ります。

空のテーブルを作成し、値の型を検証する __newindex を持つメタテーブルを設定してください。文字列以外の値を追加しようとした場合は、rawset() を使って代わりに文字列 "invalid" を格納します。文字列値の場合は、rawset() を使ってそのまま格納します。

ロジック:

  • 空のテーブルを作成する
  • テーブル、キー、値を受け取る __newindex を持つメタテーブルを作成する
  • __newindex 内で、type(value) == "string" を使って値が文字列かどうか確認する
  • 文字列の場合は、rawset(t, key, value) を使って格納する
  • 文字列ではない場合は、rawset(t, key, "invalid") を使って、代わりに "invalid" を格納する
  • setmetatable() を使ってメタテーブルをテーブルに設定する
  • rawset() を使って、初期のキーと値のペアをテーブルに追加する(メタテーブルを介さない)
  • テーブルを返す

パラメーター:

  • key(文字列): テーブルに追加する初期キー
  • value(文字列): テーブルに追加する初期値

戻り値: 初期のキーと値のペアを含み、文字列値のみを受け入れる検証機能を備えたテーブル(table)

自分で試してみよう

function createValidatedTable(key, value)
    -- ここにコードを書いてください
end
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

ロジックとプログラムの流れのすべてのレッスン

自分で練習してみよう: Luaオンラインコンパイラ