루아 메타테이블


Lua 테이블에서는 해당 키에 액세스하여 값을 얻을 수 있지만 두 테이블에서 작업을 수행할 수는 없습니다.

따라서 Lua는 테이블의 동작을 변경할 수 있는 메타테이블(Metatable)을 제공합니다.

예를 들어 메타테이블을 사용하면 Lua가 두 테이블의 추가 연산 a+b를 계산하는 방법을 정의할 수 있습니다.

Lua는 두 개의 테이블을 추가하려고 할 때 두 테이블 중 하나에 메타테이블이 있는지 먼저 확인한 다음 "__add"라는 필드가 있는지 확인하고 해당 값을 호출합니다. "__add"와 같은 실시간 필드의 경우 해당 값(종종 함수 또는 테이블)은 "메타 메서드"입니다.

메타테이블을 처리하는 데 매우 중요한 두 가지 함수가 있습니다:

  • setmetatable(table, Metatable): 지정된 테이블에 대한 메타테이블 설정(메타테이블) , __metatable 키 값이 메타테이블(메타테이블)에 존재하면 setmetatable이 실패합니다.

  • getmetatable(table): 객체의 메타테이블을 반환합니다.

다음 예는 지정된 테이블에 대한 메타테이블을 설정하는 방법을 보여줍니다.

mytable = {}                          -- 普通表 
mymetatable = {}                      -- 元表
setmetatable(mytable,mymetatable)     -- 把 mymetatable 设为 mytable 的元表

위 코드는 한 줄에 직접 작성할 수도 있습니다.

mytable = setmetatable({},{})

다음이 반환됩니다. 객체 메타테이블:

getmetatable(mytable)                 -- 这回返回mymetatable

__index 메타메서드

가장 일반적으로 사용되는 메타테이블 키입니다.

키로 테이블에 액세스할 때 키에 값이 없으면 Lua는 테이블의 메타테이블에서 __index 키를 찾습니다(메타테이블이 있다고 가정). __index에 테이블이 포함되어 있으면 Lua는 테이블에서 해당 키를 찾습니다.


lua 명령을 사용하여 대화형 모드로 들어가 볼 수 있습니다.

$ lua
Lua 5.3.0  Copyright (C) 1994-2015 Lua.org, PUC-Rio
> other = { foo = 3 } 
> t = setmetatable({}, { __index = other }) 
> t.foo
3
> t.bar
nil

__index에 함수가 포함되어 있으면 Lua는 해당 함수를 호출하고 테이블과 키는 매개변수가 함수에 전달됩니다.

__index 메타 메소드는 요소가 테이블에 존재하는지 확인합니다. 존재하지 않는 경우 반환 결과는 nil이고, 존재하는 경우 결과는 __index에 의해 반환됩니다.

mytable = setmetatable({key1 = "value1"}, {
  __index = function(mytable, key)
    if key == "key2" then
      return "metatablevalue"
    else
      return nil
    end
  end
})

print(mytable.key1,mytable.key2)

인스턴스 출력 결과:

value1	metatablevalue

인스턴스 분석:

  • mytable 테이블 할당은{key1 = "value1"}.

  • mytable은 메타테이블을 설정하며, 메타메서드는 __index입니다.

  • mytable 테이블에서 key1을 찾습니다. 발견되지 않으면 해당 요소를 반환합니다.

  • mytable 테이블에서 key2를 찾습니다. 찾을 수 없으면 메타테이블 값을 반환합니다.

  • 메타테이블에 __index 메서드가 있는지 확인합니다. __index 메서드가 함수인 경우 해당 함수를 호출합니다.

  • 메타 메소드에서 "key2" 키 매개변수가 전달되었는지 확인합니다(mytable.key2가 설정됨). "key2" 매개변수가 전달되면 "metatablevalue" 그렇지 않으면 mytable의 해당 값이 반환됩니다.


위 코드를 다음과 같이 간단하게 작성할 수 있습니다.

mytable = setmetatable({key1 = "value1"}, { __index = { key2 = "metatablevalue" } })
print(mytable.key1,mytable.key2)

요약

루아가 테이블을 검색할 때 요소 규칙은 실제로 다음 3단계입니다.

  • 1. 요소를 찾으면 해당 요소를 반환합니다.

  • 2. 테이블에 메타테이블이 있는지 확인합니다. 메타테이블이 있으면 nil을 반환합니다.

  • 3. 메타테이블에 __index 메서드가 있는지 확인합니다. __index 메서드가 nil이면 nil을 반환하고, __index 메서드가 테이블이면 1, 2, 3을 반복합니다. __index 메서드가 함수인 경우 해당 함수의 반환 값이 반환됩니다.


__newindex 메타 메서드

__newindex 메타 메서드는 테이블을 업데이트하는 데 사용되며 __index는 테이블에 액세스하는 데 사용됩니다.

테이블의 누락된 인덱스에 값을 할당하면 인터프리터는 __newindex 메타메서드를 찾습니다. 존재하는 경우 할당을 수행하지 않고 이 함수를 호출합니다.

다음 예에서는 __newindex 메타 메서드의 적용을 보여줍니다.

mymetatable = {}
mytable = setmetatable({key1 = "value1"}, { __newindex = mymetatable })

print(mytable.key1)

mytable.newkey = "新值2"
print(mytable.newkey,mymetatable.newkey)

mytable.key1 = "新值1"
print(mytable.key1,mymetatable.key1)

위 예의 실행 출력은 다음과 같습니다.

value1
nil	新值2
新值1	nil

위 예에서 메타- __newindex 메소드가 테이블에 설정되어 있습니다. 새 인덱스 키(newkey)에 값을 할당하면(mytable.newkey = "new value 2") 값을 할당하지 않고 메타메서드가 호출됩니다. 기존 인덱스 키(key1)가 할당되면 메타 메서드 __newindex를 호출하지 않고 값이 할당됩니다.

다음 예에서는 rawset 함수를 사용하여 테이블을 업데이트합니다.

mytable = setmetatable({key1 = "value1"}, {
  __newindex = function(mytable, key, value)
		rawset(mytable, key, "\""..value.."\"")

  end
})

mytable.key1 = "new value"
mytable.key2 = 4

print(mytable.key1,mytable.key2)

위 예의 실행 출력은 다음과 같습니다.

new value	"4"

테이블에 연산자 추가

다음 예제에서는 두 테이블의 추가 작업을 보여줍니다.

-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 两表相加操作
mytable = setmetatable({ 1, 2, 3 }, {
  __add = function(mytable, newtable)
    for i = 1, table_maxn(newtable) do
      table.insert(mytable, table_maxn(mytable)+1,newtable[i])
    end
    return mytable
  end
})

secondtable = {4,5,6}

mytable = mytable + secondtable
	for k,v in ipairs(mytable) do
print(k,v)
end

위 예제 실행의 출력 결과는 다음과 같습니다.

1	1
2	2
3	3
4	4
5	5
6	6

__add 키가 메타테이블에 포함됩니다. 그리고 덧셈 연산이 수행됩니다. 테이블의 해당 작업 목록은 다음과 같습니다.

模式描述
__add对应的运算符 '+'.
__sub对应的运算符 '-'.
__mul对应的运算符 '*'.
__div对应的运算符 '/'.
__mod对应的运算符 '%'.
__unm对应的运算符 '-'.
__concat对应的运算符 '..'.
__eq对应的运算符 '=='.
__lt对应的运算符 '<'.
__le对应的运算符 '<='.

__call 메타메서드

__call 메타메서드는 Lua가 값을 호출할 때 호출됩니다. 다음 예에서는 테이블의 요소 합계 계산을 보여줍니다.

-- 计算表中最大值,table.maxn在Lua5.2以上版本中已无法使用
-- 自定义计算表中最大值函数 table_maxn
function table_maxn(t)
    local mn = 0
    for k, v in pairs(t) do
        if mn < k then
            mn = k
        end
    end
    return mn
end

-- 定义元方法__call
mytable = setmetatable({10}, {
  __call = function(mytable, newtable)
	sum = 0
	for i = 1, table_maxn(mytable) do
		sum = sum + mytable[i]
	end
    for i = 1, table_maxn(newtable) do
		sum = sum + newtable[i]
	end
	return sum
  end
})
newtable = {10,20,30}
print(mytable(newtable))

위 예의 실행 출력은 다음과 같습니다.

70

__tostring 메타메서드

__tostring 메타메소드는 테이블의 출력 동작을 수정하는 데 사용됩니다. 다음 예에서는 테이블의 출력 내용을 사용자 정의했습니다.

mytable = setmetatable({ 10, 20, 30 }, {
  __tostring = function(mytable)
    sum = 0
    for k, v in pairs(mytable) do
		sum = sum + v
	end
    return "表所有元素的和为 " .. sum
  end
})
print(mytable)

위 예를 실행한 결과는 다음과 같습니다.

表所有元素的和为 60

이 기사에서 우리는 메타테이블은 코드 기능을 크게 단순화할 수 있습니다. 따라서 Lua의 메타테이블을 이해하면 더 간단하고 더 나은 Lua 코드를 작성할 수 있습니다.