메타테이블은 테이블이 이전보다 더 강력하게 되도록 합니다. 데이터에 부착되며 메타메서드라고 하는 값을 포함합니다. 메타메서드는 특정 액션이 데이터에 부착되면 실행됩니다.
다음 코드를 고려하십시오.
local list = {1, 2}print(list[3])
이 코드는 목록의 3번째 인덱스를 검색하고 아무것도 찾지 않고 반환하는 일반적인 코드입니다. 그러나 그렇지 않습니다. 코드는 목록을 검색하고 3번째 인덱스를 찾고 아무것도 찾지 않고 반환하며, 없는 경우 메타테이블을 반환합니다.
메타테이블 조작
테이블의 메타테이블을 추가하고 찾는 두 가지 주요 함수는 setmetatable 및 getmetatable입니다.
local x = {}local metaTable = {} -- 메타 테이블은 테이블이기도 합니다!setmetatable(x, metaTable) -- 메타테이블이라는 이름의 x개의 메타테이블을 주세요!print(getmetatable(x)) --> table: [hexadecimal memory address]
setmetatable 함수는 또한 메이블을 설정하는 테이블을 반환하므로 이 두 스크립트는 동일한 작업을 수행합니다.
local x = {}setmetatable(x, {})
local x = setmetatable({}, {})
메타메서드
메타메서드는 메타테이블 내에 저장된 함수입니다. 테이블을 호출하거나 테이블을 추가하거나 테이블을 나눔으로써 메타메서드를 사용할 수 있습니다. 여기 메타메서드 목록이 있습니다:
메서드 | 설명 |
---|---|
__index(테이블, 인덱스) | 테이블이 인덱싱되면 발생하는 테이블[index], 즉 테이블이 없으면 발생합니다. 또한 테이블에 설정할 수도 있습니다. 이 경우 해당 테이블은 인덱싱됩니다. |
__newindex(테이블, 인덱스, 값) | 테이블[index]이 설정되려 시도할 때 발생합니다(테이블[index] = 값), 테이블[index]이 없으면 설정할 수도 있습니다. 또한 테이블[index]을 지정할 수도 있습니다. 이 경우 해당 테이블은 인덱싱됩니다. |
__call(테이블, ...) | 테이블이 함수처럼 호출되면 발생합니다. ... 패스된 인수입니다. |
__concat(테이블, 값) | 테이블에서 .. 연산자 콘센트를 사용할 때 발생합니다. |
__unm(테이블) | 테이블에서 unary - 연산자를 사용할 때 발생합니다.Fires when the unary - operator is used on the table. |
__add(테이블, 값) | 플러스 연산자입니다. |
__sub(테이블, 값) | – 뺄셈 연산자입니다. |
__mul(테이블, 값) | 다음 * 배율 연산자입니다. |
__div(테이블, 값) | / division 연산자. |
__idiv(테이블, 값) | // 바닥 분할 연산자입니다. |
__mod(테이블, 값) | %modulus 연산자입니다. |
__pow(테이블, 값) | ^ 지수 부동 소수점 연산자입니다. |
__tostring(테이블) | 테이블에서 문자열을 처리할 때 발생합니다.Fired when tostring is called on the table. |
__metable | 현재가 있으면 메타테이블을 잠그고 metatable이 메타테이블을 대신 반환하므로 메타테이블이 오류가 아닌 대신 설정되지 않습니다. 메타값이 아닙니다. |
__eq(테이블, 값) | == 연산자¹와 같습니다. |
__lt(테이블, 값) | < 작은 연산자¹ |
__le(테이블, 값) | The << 연산자¹ |
__모드 | 약한 테이블에서 사용되어 테이블의 키 및/또는 값이 약한지 여부를 선언합니다. 참고: Roblox 인스턴스에 대한 참조는 결코 약하지 않습니다. 이러한 참조를 가진 테이블은 잠깐 수 없는 가비지를 수집하지 않습니다. |
__gc(테이블) | 테이블이 가비지 수집되었을 때 발생합니다. 참고: Roblox에서 이 메타메서드는 사용할 수 없습니다. |
__len(테이블) | 개체에서 #길이 연산자를 사용할 때 발생합니다. |
__iter(테이블) | 일반화된 반복기를 사용할 때 사용자 정의 반복기를 나타내는 데 사용됩니다. |
함수를 작성할 때 메타메서드 기능을 사용하는 경우 메타메서드 기능 간에 두 함수 매개 변수가 서로 교체할 수 있습니다. 예를 들어, 스칼라 분할을 사용하여 벡터 작업을 수행할 때 매개 변수 간에 상호 작용이 없는 경우 모든 시나리오를 고려해야 합니다
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
-- 둘 중 하나 또는 둘 다 벡터
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.333333333333, 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를 인덱싱하지 못하고 발견되지 않았을 때 발생했습니다. Lua는 테이블에서 __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 메타메서드를 사용하여 간단하게 만들 것입니다.
local function mathProblem(num)
for i = 1, 20 do
num = math.floor(num * 10 + 65)
end
for i = 1, 10 do
num += i - 1
end
return num
end
local metatable = {
__index = function(object, key)
local num = mathProblem(key)
object[key] = num
return num
end
}
local t = setmetatable({}, metatable)
print(t[1]) -- 이 숫자를 처음 사용하기 때문에 속도가 느리게 됩니다. 그래서 수학 함수를 실행해야 합니다.
print(t[2]) -- 이 숫자를 사용하는 것이 처음이기 때문에 속도가 느려집니다.
print(t[1]) -- will be fast because it's just grabbing the number from the table.
원시, 원시, 원시 등
메타테이블을 플레이할 때 문제가 발생할 수 있습니다. 테이블에 새 값을 생성하려면 메타메서드 __index 를 사용해야 하지만, 해당 메타메서드에 __newindex 메타메서드가
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 C-Stack overflow
이제 왜 그게 스택 오버플로우를 일으키는가? 스택 오버플로우는 함수를 너무 자주 호출하는 경우 발생합니다. 그러나 그게 발생하게 하는 이유는 무엇인가요? __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]) -- prints 10
집합 데이터 형식 사용
A set 는 순서가 없고 중복되지 않은 항목의 컬렉션입니다. 항목이 있거나 없거나 설정내에 포함되지 않은 하나의 항목입니다. 메타 테이블을 사용하여 집합 내의 컬렉션을 구성하고 조작할 수 있습니다.
기본 메서드
다음 코드에는 기본 집합 기능이 포함되어 있으므로 새 집합을 생성하고 아이템을 추가하고 제거하고 집합에 아이템이 포함되어 있는지 확인하고 설정콘텐츠를 출력합니다.
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
유니온
다음 함수를 사용하여 2개의 세트의 유니언을 얻을 수 있습니다, 즉 쌍의 항목을 모두 포함하는 컬렉션을 모두 없는 경우 컬렉션 간의 연산자 간의 연산자 간의 연산자 간의 연산자 간의 연산자 간의 연산자 간의 연산자 간의 연산자 간의 연산자 간의
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 메서 하위 집합 바로가기를 제공하는 하위 집합 메서드를 사용합니다.
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