수색…
통사론
- [[local] mt =] getmetatable ( t ) -> ' t '와 연관된 metatable을 검색합니다.
- [로컬] t =] setmetatable (t, MT) -> '산'에서 't'복귀 't'에 대한 메타 세트
매개 변수
매개 변수 | 세부 |
---|---|
티 | 루아 표를 참조하는 변수; 테이블 리터럴이 될 수도 있습니다. |
후지산 | 메타 테이블로 사용할 테이블; 0 개 이상의 메타 메소드 필드를 설정할 수 있습니다. |
비고
여기에 언급되지 않은 일부 메타 메소드가 있습니다. 전체 목록과 사용법은 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
메타 메소드입니다. 또한 표가 될 수 있으며 조회로 사용됩니다. 이것은 루아에서 클래스를 만드는 데 아주 일반적으로 사용됩니다. 여기서 테이블 (종종 metatable 자체)은 클래스의 모든 연산 (메소드)을 저장하는 데 사용됩니다.
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
metamethod를 사용할 수 있습니다. __gc
metamethod는 객체가 파괴 될 때 객체를 인수로 호출합니다. 이 메타 메서드를 일종의 소멸자로 볼 수 있습니다.
이 예제는 작동중인 __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
이라는 메타 메서드가 있습니다.이 메타 메서드는 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")
표의 색인 생성
아마도 메타 테이블의 가장 중요한 사용은 테이블의 인덱싱을 변경할 가능성이 있습니다. 이를 위해 콘텐츠를 읽고 표의 내용을 쓰는 두 가지 작업을 고려해야합니다. 테이블에 해당 키가 없으면 두 조치 모두 트리거됩니다.
독서
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
이제 실제 값이 테이블에 어떻게 쓰여지는지 자문 해보십시오. 이 경우에는 그렇지 않습니다. 여기서 문제는 메타 메소드가 메타 메소드를 트리거 할 수 있다는 것입니다.이 메소드는 부정 루프 또는 더 정확하게는 스택 오버 플로우를 초래합니다. 그러면 어떻게 해결할 수 있을까요? 이에 대한 솔루션을 원시 테이블 액세스 라고 합니다 .
원시 테이블 액세스
때로는 메타 메소드를 트리거하고 싶지 않지만 액세스를 둘러싼 영리한 기능을 사용하지 않고 주어진 키를 실제로 쓰거나 읽는 것이 좋습니다. 이를 위해 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])
이를 통해 ower 이전 __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