메타테이블을 사용하면 테이블이 이전보다 강력해질 수 있습니다.데이터에 연결되며 metamethods라는 값을 포함합니다.메타메소드는 특정 작업이 부착된 데이터와 함께 사용될 때 발생합니다.
메타테이블 조작
테이블의 메타테이블을 추가하고 찾는 두 가지 주요 기능은 setmetatable() 및 getmetatable()입니다.
local x = {}local metaTable = {} -- 메타테이블은 또한 테이블입니다!setmetatable(x, metaTable) -- x에 metaTable이라는 메타테이블을 주세요!print(getmetatable(x)) --> table: [hexadecimal memory address]
setmetatable() 함수는 또한 설정 중인 메타테이블의 테이블을 반환하므로 이 두 스크립트는 동일한 작업을 수행합니다.
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
메타메소드
메타메소드는 메타테이블 내에 저장된 함수입니다.테이블을 호출하여 테이블을 추가하거나 테이블을 분할하는 것까지도 가능합니다.사용 가능한 메타메소드 목록입니다:
메서드 | 설명 |
---|---|
__index(table, index) | 가 인덱싱될 때 발생하며, 가 인덱싱될 경우 해당 테이블이 인덱싱됩니다. 또한 테이블로 설정할 수 있으며, 이 경우 해당 테이블이 인덱싱됩니다. |
__newindex(table, index, value) | 가 설정되려고 시도하면 화재가 발생하고, 가 이면 화재가 발생합니다.테이블로 설정할 수도 있으며, 이 경우 해당 테이블이 인덱싱됩니다. |
__call(table, ...) | 테이블이 함수처럼 호출될 때 발생하는 화재, ...는 전달된 인수입니다. |
__concat(table, value) | 테이블에서 .. 연산자를 사용할 때 발생합니다. |
__unm(table) | 테이블에서 단일 – 연산자를 사용할 때 발생합니다. |
__add(table, value) | + 더하기 연산자. |
__sub(table, value) | – 뺄셈 연산자. |
__mul(table, value) | * 곱하기 연산자. |
__div(table, value) | / 분할 연산자. |
__idiv(table, value) | // 층 분할 연산자. |
__mod(table, value) | % 모듈 연산자. |
__pow(table, value) | ^ 곱셈 연산자. |
__tostring(table) | 문자열이 테이블에서 호출될 때 발사됩니다. |
__metatable | 현재 있으면 metatable을 잠겨서 getmetatable()가 메타테이블 대신 이것을 반환하고 setmetatable()는 오류가 발생합니다. 기능 값이 아닙니다. |
__eq(table, value) | 연산자¹와 같은 == 동일 |
__lt(table, value) | < 덜 연산자¹ |
__le(table, value) | <= 연산자¹ |
__mode | 약한 테이블에서 사용하여 테이블의 키 및/또는 값이 약한지 여부를 선언합니다.Roblox 인스턴스에 대한 참조는 결코 약하지 않습니다.이러한 참조를 보유하는 테이블은 결코 가비지 수집되지 않습니다. |
__len(table) | 개체에서 # 길이 연산자가 사용되면 발생합니다. |
__iter(table) | 일반화된 반복을 사용할 때 사용자 지정 반복기를 나타내는 데 사용됩니다. |
아리트메틱 또는 관계 메타메소드에 대한 함수를 작성할 때 두 함수 매개변수는 메타메소드가 발생한 테이블과 다른 값 사이에서 교환할 수 있어야 합니다.예를 들어, 스칼라 분할로 벡터 작업을 수행할 때 곱셈이 아닙니다.따라서 자체 클래스에 대한 메타메소드를 작성하는 경우 두 시나리오를 모두 고려해야 합니다.
local vector2 = {__type = "vector2"}
local mt = {__index = vector2}
function mt.__div(a, b)
if type(a) == "number" then
-- a는 스칼라, b는 벡터
local scalar, vector = a, b
return vector2.new(scalar / vector.x, scalar / vector.y)
elseif type(b) == "number" then
-- a는 벡터, b는 스칼라입니다
local vector, scalar = a, b
return vector2.new(vector.x / scalar, vector.y / scalar)
elseif (a.__type and a.__type == "vector2" and b.__type and b.__type == "vector2") then
-- a와 b가 벡터입니다
return vector2.new(a.x / b.x, a.y / b.y)
end
end
function mt.__tostring(t)
return t.x .. ", " .. t.y
end
function vector2.new(x, y)
local self = setmetatable({}, mt)
self.x = x or 0
self.y = y or 0
return self
end
local a = vector2.new(10, 5)
local b = vector2.new(-3, 4)
print(a / b) -- -3.3333333333333, 1.25
print(b / a) -- -0.3, 0.8
print(2 / a) -- 0.2, 0.4
print(a / 2) -- 5, 2.5
사용
예를 들어, __unm 메타메소드를 사용하여 테이블을 부정적으로 만드는 방법은 많이 있습니다:
local metatable = {
__unm = function(t) -- __unm은 단일 - 연산자에 대한 것입니다
local negated = {}
for key, value in t do
negated[key] = -value -- 이 테이블의 모든 값을 부정합니다
end
return negated -- 테이블 반환
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
print(table.concat(-table1, "; ")) --> -10; -11; -12
여기 흥미로운 방법으로 사물을 선언하는 방법이 있습니다 __index :
local metatable = {__index = {x = 1}}local t = setmetatable({}, metatable)print(t.x) --> 1
__index 는 테이블에서 x 가 인덱싱되어 찾지 못했을 때 발사되었습니다.Luau는 그런 다음 __index 테이블을 통해 인덱스 x 이라고 불리는 것을 검색하고, 하나를 찾아 반환했습니다.
이제 간단한 함수로 쉽게 할 수 있지만, 그것이 어디에서 왔는지 훨씬 더 많이 있습니다. 이것을 예로 들어 보겠습니다:
local t = {10, 20, 30}print(t(5))
일반적으로 테이블을 호출할 수 없지만, 메타테이블을 사용하면 다음을 수행할 수 있습니다.
local metatable = {
__call = function(t, param)
local sum = {}
for i, value in ipairs(t) do
sum[i] = value + param -- 인수 (5)를 값에 추가한 다음 새 테이블(t)에 배치합니다.
end
return unpack(sum) -- 개별 테이블 값 반환
end
}
local t = setmetatable({10, 20, 30}, metatable)
print(t(5)) --> 15 25 35
테이블 추가와 같은 다른 작업도 많이 수행할 수 있습니다:
local table1 = {10, 11, 12}local table2 = {13, 14, 15}for k, v in table1 + table2 doprint(k, v)end
이렇게 하면 테이블에서 산술 연산을 수행하려고 하지만 메타테이블로 시도하면 작동한다고 오류가 발생합니다.
local metatable = {
__add = function(t1, t2)
local sum = {}
for key, value in t1 do
sum[key] = value
end
for key, value in t2 do
if sum[key] then
sum[key] += value
else
sum[key] = value
end
end
return sum
end
}
local table1 = setmetatable({10, 11, 12}, metatable)
local table2 = setmetatable({13, 14, 15}, metatable)
for k, v in table1 + table2 do
print(k, v)
end
메타테이블로 플레이할 때 몇 가지 문제에 직면할 수 있습니다.테이블에서 새 값을 생성하기 위해 __index 메타메소드를 사용해야 하지만, 해당 테이블의 메타테이블에도 __newindex 메타메소드가 있으면, Luau 내장 함수 rawset() 를 사용하여 메타메소드를 호출하지 않고 값을 설정하려고 합니다.이 함수를 사용하지 않으면 발생하는 일의 예로 다음 코드를 사용하십시오.
local t = setmetatable({}, {
__index = function(self, i)
self[i] = i * 10
return self[i]
end,
__newindex = function(self, i, v)
-- 일반 방식으로 값을 테이블에 설정하지 마십시오
end
})
print(t[1]) -- Causes a stack overflow
스택 오버플로는 함수를 너무 많이 호출하려고 할 때 발생합니다.위의 __index 함수에서 self[i] 는 값으로 설정되어 있으므로 다음 줄에 도달하면 self[i] 가 존재해야 하고 아마도 메타메소드 __index 를 호출하지 않을 것입니다.문제는 __newindex 값을 설정할 수 없다는 것입니다.그 존재는 표준 t[i] = v 방법으로 값이 테이블에 추가되는 것을 중지합니다.이를 통과하려면 rawset() 함수를 사용하십시오:
local t = setmetatable({}, {
__index = function(self, i)
rawset(self, i, i * 10)
return self[i]
end,
__newindex = function(self, i, v)
-- 일반 방식으로 값을 테이블에 설정하지 마십시오
end
})
print(t[1]) --> 10
설정된 데이터 유형 사용
A 세트 는 순서가 없고 중복 요소가 없는 아이템 모음입니다.아이템이 는 이거나 는 설정내에 포함되지 않습니다.메타테이블을 사용하여 Luau 스크립트 내에서 세트를 생성하고 조작할 수 있습니다.
기본 메서드
다음 코드에는 기본 집합 기능이 포함되어 있으며, 새 집합을 만들고, 아이템을 추가하고, 집합에 아이템이 포함되는지 확인하고, 설정내용을 출력할 수 있습니다.
local Set = {}
Set.__index = Set
-- 선택적 항목 목록에서 세트를 생성하는 함수
function Set.new(items)
local newSet = {}
for key, value in items or {} do
newSet[value] = true
end
return setmetatable(newSet, Set)
end
-- 설정항목을 추가하는 함수
function Set:add(item)
self[item] = true
end
-- 설정항목을 제거하는 함수
function Set:remove(item)
self[item] = nil
end
-- 집합에 아이템포함되는지 확인하는 함수
function Set:contains(item)
return self[item] == true
end
-- 디버깅을 위해 쉼표로 구분된 목록으로 출력하는 함수
function Set:output()
local elems = {}
for key, value in self do
table.insert(elems, tostring(key))
end
print(table.concat(elems, ", "))
end
세트 설정
새로운 세트는 추가할 옵циональ 항목 배열로 Set.new()를 호출하여 생성할 수 있습니다.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
정의에 따라 집합에는 순서 개념이 없습니다.
항목 아이템
기존 세트에 아이템을 추가하는 작업은 Set:add() 메서드를 통해 수행할 수 있습니다.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:add("Mango")
항목 아이템
설정항목을 제거하려면 항목 이름으로 Set:remove()를 호출하십시오.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})fruits:remove("Orange")
항목 아이템
집합에 특정 아이템포함되어 있는지 확인하려면 Set:contains()를 사용하십시오.
local fruits = Set.new({"Apple", "Lemon", "Orange", "Cherry", "Lime", "Peach"})local result1 = fruits:contains("Cherry")print(result1) -- 참local result2 = fruits:contains("Watermelon")print(result2) -- false
추가 메서드
세트에 대한 다른 유용한 작업을 구현하여 세트 간에 항목을 비교하거나, 세트를 결합하거나, 한 세트를 다른 세트에서 뺄 수 있습니다.
교차점
집합을 Venn 다이어그램으로 고려할 때, 다음과 같이 두 집합의 교집합을 얻을 수 있으며, 이는 두 집합에 나타나는 항목을 의미합니다.
local function getIntersection(set1, set2)
local result = Set.new()
for key, value in set1 do
if set2:contains(key) then
result:add(key)
end
end
return result
end
local freshFruits = Set.new({"Mango", "Lemon", "Orange", "Cherry", "Lime", "Peach"})
local frozenFruits = Set.new({"Mango", "Peach", "Pineapple"})
local commonFruits = getIntersection(freshFruits, frozenFruits)
commonFruits:output() -- Mango, Peach
연합
다음 함수로 두 세트의 연합 을 얻을 수 있으며, 이는 중복 없는 두 세트의 항목 집합을 의미합니다.이 함수는 메타블 __add 메서드를 사용하여 추가 단축키를 제공합니다 set1 + set2 .
function Set:__add(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = true
end
return result
end
local sweetFruits = Set.new({"Apple", "Mango", "Cherry", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local allFruits = sweetFruits + sourFruits
allFruits:output() -- 피치, 라임, 애플, 체리, 레몬, 망고
빼기
다음 함수를 통해 다른 세트의 항목에서 하나의 세트에 있는 모든 항목을 제거할 수 있습니다.위의 함수와 비슷하게, 이는 metatable __sub 메서드를 사용하여 뺄셈 단축키를 set1 - set2 제공합니다.
function Set:__sub(otherSet)
local result = Set.new()
for entry in self do
result[entry] = true
end
for entry in otherSet do
result[entry] = nil
end
return result
end
local allFruits = Set.new({"Apple", "Lemon", "Mango", "Cherry", "Lime", "Peach"})
local sourFruits = Set.new({"Lemon", "Lime"})
local sweetFruits = allFruits - sourFruits
sweetFruits:output() -- Mango, Apple, Cherry, Peach