サーチ…
構文
- [[local] mt =] getmetatable( t ) - > ' t 'に関連付けられたメタテーブルを取得する
- [ローカル] T =] setmetatable(T、MT) - > 'MT'戻る'T'と'T'のメタテーブルを設定します
パラメーター
パラメータ | 詳細 |
---|---|
t | ルアテーブルを参照する変数。テーブルリテラルにすることもできます。 |
mt | メタテーブルとして使用するテーブル。 0個以上のメタメソッドフィールドを設定できます。 |
備考
ここで言及されていないいくつかのメタメソッドがあります。完全なリストとその使用方法については、 luaマニュアルの対応するエントリを参照してください。
メタテーブルの作成と使用
メタテーブルは、luaオブジェクトの動作を変更する一連の操作を定義します。メタテーブルは通常のテーブルで、特別な方法で使用されます。
local meta = { } -- create a table for use as metatable
-- a metatable can change the behaviour of many things
-- here we modify the 'tostring' operation:
-- this fields should be a function with one argument.
-- it gets called with the respective object and should return a string
meta.__tostring = function (object)
return string.format("{ %d, %d }", object.x, object.y)
end
-- create an object
local point = { x = 13, y = -2 }
-- set the metatable
setmetatable(point, meta)
-- since 'print' calls 'tostring', we can use it directly:
print(point) -- prints '{ 13, -2 }'
テーブルをメタメソッドとして使用する
いくつかのメタメソッドは関数である必要はありません。最も重要な例は__index
です。ルックアップとして使用されるテーブルにすることもできます。これは、luaでクラスを作成する際によく使用されます。ここでは、クラスのすべての操作(メソッド)を保持するためにテーブル(多くの場合、メタデータ自体)が使用されます。
local meta = {}
-- set the __index method to the metatable.
-- Note that this can't be done in the constructor!
meta.__index = meta
function create_new(name)
local self = { name = name }
setmetatable(self, meta)
return self
end
-- define a print function, which is stored in the metatable
function meta.print(self)
print(self.name)
end
local obj = create_new("Hello from object")
obj:print()
ガベージコレクタ - __gcメタメソッド
ルアのオブジェクトはガベージコレクションされます。場合によっては、オブジェクトが破壊(収集)されたときに、リソースを解放したり、メッセージを印刷したり、何か他のことをしたりする必要があります。このためには、 __gc
gcメタメソッドを使うことができます__gc
gcメタメソッドは、オブジェクトが破棄されたときにオブジェクトを引数として呼び出されます。あなたはこのメタメソッドを一種のデストラクタとして見ることができます。
この例は、実際の__gc
メタメソッドを示しています。 t
に割り当てられた内部テーブルがガベージコレクションされると、収集される前にメッセージが出力されます。同様に、スクリプトの終わりに達したときに外部テーブル用:
local meta =
{
__gc = function(self)
print("destroying self: " .. self.name)
end
}
local t = setmetatable({ name = "outer" }, meta)
do
local t = { name = "inner" }
setmetatable(t, meta)
end
より多くのメタメソッド
==や#=のような基本的な演算もありますが、それ以外にも多くのメタメソッドがあり、その中には算術演算(加算、減算、乗算など)、ビット演算(and、or、xor、shift) (平等と長さ)。これらの操作の多くをサポートするクラスを構築することができます:有理算術の呼び出し。これは非常に基本的なものですが、そのアイディアを示しています。
local meta = {
-- string representation
__tostring = function(self)
return string.format("%s/%s", self.num, self.den)
end,
-- addition of two rationals
__add = function(self, rhs)
local num = self.num * rhs.den + rhs.num * self.den
local den = self.den * rhs.den
return new_rational(num, den)
end,
-- equality
__eq = function(self, rhs)
return self.num == rhs.num and self.den == rhs.den
end
}
-- a function for the creation of new rationals
function new_rational(num, den)
local self = { num = num, den = den }
setmetatable(self, meta)
return self
end
local r1 = new_rational(1, 2)
print(r1) -- 1/2
local r2 = new_rational(1, 3)
print(r1 + r2) -- 5/6
local r3 = new_rational(1, 2)
print(r1 == r3) -- true
-- this would be the behaviour if we hadn't implemented the __eq metamethod.
-- this compares the actual tables, which are different
print(rawequal(r1, r3)) -- false
テーブルを呼び出し可能にする
__call
と呼ばれるメタメソッドがあります。このメタメソッドは、関数として使用されたときにオブジェクトのbevahiourを定義しobject()
例: object()
。これは関数オブジェクトの作成に使用できます:
-- create the metatable with a __call metamethod
local meta = {
__call = function(self)
self.i = self.i + 1
end,
-- to view the results
__tostring = function(self)
return tostring(self.i)
end
}
function new_counter(start)
local self = { i = start }
setmetatable(self, meta)
return self
end
-- create a counter
local c = new_counter(1)
print(c) --> 1
-- call -> count up
c()
print(c) --> 2
metamethodは対応するオブジェクトとともに呼び出され、残りの引数はすべてその関数に渡されます。
local meta = {
__call = function(self, ...)
print(self.prepend, ...)
end
}
local self = { prepend = "printer:" }
setmetatable(self, meta)
self("foo", "bar", "baz")
表の索引付け
おそらく、メタテーブルの最も重要な使用は、テーブルの索引付けを変更する可能性です。そのためには、コンテンツを読み 、テーブルの内容を書き込むという2つの処置があります。両方のアクションは、対応するキーがテーブルに存在しない場合にのみトリガされることに注意してください。
読書
local meta = {}
-- to change the reading action, we need to set the '__index' method
-- it gets called with the corresponding table and the used key
-- this means that table[key] translates into meta.__index(table, key)
meta.__index = function(object, index)
-- print a warning and return a dummy object
print(string.format("the key '%s' is not present in object '%s'", index, object))
return -1
end
-- create a testobject
local t = {}
-- set the metatable
setmetatable(t, meta)
print(t["foo"]) -- read a non-existent key, prints the message and returns -1
これは、存在しないキーを読み取っている間にエラーを発生させるために使用できます。
-- raise an error upon reading a non-existent key
meta.__index = function(object, index)
error(string.format("the key '%s' is not present in object '%s'", index, object))
end
書き込み
local meta = {}
-- to change the writing action, we need to set the '__newindex' method
-- it gets called with the corresponding table, the used key and the value
-- this means that table[key] = value translates into meta.__newindex(table, key, value)
meta.__newindex = function(object, index, value)
print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
value, object, index))
--object[index] = value -- we can't do this, see below
end
-- create a testobject
local t = { }
-- set the metatable
setmetatable(t, meta)
-- write a key (this triggers the method)
t.foo = 42
実際の値がテーブルにどのように書き込まれているかを今すぐ尋ねることができます。この場合、そうではありません。ここでの問題は、メタメソッドがメタメソッドをトリガできることです。このメソッドは、不定ループ、より正確にはスタックオーバーフローを引き起こします。では、どうすればこの問題を解決できますか?このためのソリューションは、 RAWテーブルアクセスと呼ばれています 。
ローテーブルアクセス
場合によっては、メタメソッドをトリガーしたくない場合もありますが、実際にアクセスしたときに巧妙な関数を使うことなく、指定されたキーを正確に書き込んだり、読み込んだりすることがあります。このため、luaはローテーブルへのアクセス方法を提供します:
-- first, set up a metatable that allows no read/write access
local meta = {
__index = function(object, index)
-- raise an error
error(string.format("the key '%s' is not present in object '%s'", index, object))
end,
__newindex = function(object, index, value)
-- raise an error, this prevents any write access to the table
error(string.format("you are not allowed to write the object '%s'", object))
end
}
local t = { foo = "bar" }
setmetatable(t, meta)
-- both lines raise an error:
--print(t[1])
--t[1] = 42
-- we can now circumvent this problem by using raw access:
print(rawget(t, 1)) -- prints nil
rawset(t, 1, 42) -- ok
-- since the key 1 is now valid, we can use it in a normal manner:
print(t[1])
これで、以前の__newindex
以前の__newindex
メソッドを書き換えて、実際に値をテーブルに書き込むことができます:
meta.__newindex = function(object, index, value)
print(string.format("writing the value '%s' to the object '%s' at the key '%s'",
value, object, index))
rawset(object, index, value)
end
OOPのシミュレーション
local Class = {} -- objects and classes will be tables
local __meta = {__index = Class}
-- ^ if an instance doesn't have a field, try indexing the class
function Class.new()
-- return setmetatable({}, __meta) -- this is shorter and equivalent to:
local new_instance = {}
setmetatable(new_instance, __meta)
return new_instance
end
function Class.print()
print "I am an instance of 'class'"
end
local object = Class.new()
object.print() --> will print "I am an instance of 'class'"
インスタンスメソッドは、最初の引数としてオブジェクトを渡すことで記述できます。
-- append to the above example
function Class.sayhello(self)
print("hello, I am ", self)
end
object.sayhello(object) --> will print "hello, I am <table ID>"
object.sayhello() --> will print "hello, I am nil"
これには構文上の砂糖がいくつかあります。
function Class:saybye(phrase)
print("I am " .. self .. "\n" .. phrase)
end
object:saybye("c ya") --> will print "I am <table ID>
--> c ya"
クラスにデフォルトフィールドを追加することもできます。
local Class = {health = 100}
local __meta = {__index = Class}
function Class.new() return setmetatable({}, __meta) end
local object = Class.new()
print(object.health) --> prints 100
Class.health = 50; print(object.health) --> prints 50
-- this should not be done, but it illustrates lua indexes "Class"
-- when "object" doesn't have a certain field
object.health = 200 -- This does NOT index Class
print(object.health) --> prints 200