English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Lua 메타테이블(Metatable)

Lua 테이블에서는 특정 키를 통해 값을 가져올 수 있지만, 두 테이블을 조작할 수는 없습니다.

따라서 Lua는 테이블의 행동을 변경할 수 있는 메타테이블을 제공하며, 각 행동은 해당 메타 메서드와 연결됩니다.

예를 들어, 메타테이블을 사용하여 Lua가 두 테이블의 더하기 연산을 어떻게 계산할지 정의할 수 있습니다.+b。

Lua가 두 테이블을 더하는 시도를 할 때, 먼저 두 테이블 중 하나에 메타테이블이 있는지 확인하고, 그 다음 "__add" 필드가 있는지 확인합니다. "__add"과 같은 즉시 필드의 값을 호출하면 "메타 메서드"라고 합니다. 메타 메서드의 값을 호출하면 "메타 메서드"라고 합니다.

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

  • setmetatable(table, metatable): 특정 테이블에 메타테이블(metatable)을 설정할 때, 메타테이블에 __metatable 키가 존재하면 setmetatable은 실패합니다.

  • getmetatable(table): 반환된 객체 메타테이블(metatable)

다음 예제는 특정 테이블에 메타테이블을 설정하는 방법을 보여줍니다:

mytable = {}                          -- 일반 테이블 
mymetatable = {}                      -- 메타테이블
setmetatable(mytable, mymetatable)     -- mymetatable을 mytable의 메타테이블로 설정합니다

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

mytable = setmetatable({}, {})

다음은 반환된 객체 메타테이블입니다:

getmetatable(mytable)                 -- 이번에는 mymetatable을 반환합니다

__index 원본 메서드

이것은 metatable에서 가장 자주 사용되는 키입니다.

당신이 테이블에 키를 통해 접근할 때, 그 키에 값이 없다면 Lua는 테이블의 metatable(존재하는 경우)의 __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에서 key를 검색합니다1찾으면 해당 요소를 반환하고, 찾지 못하면 계속 진행합니다。

  • mytable에서 key를 검색합니다2찾으면 metatablevalue를 반환하고, 찾지 못하면 계속 진행합니다。

  • 메타테이블에 __index 메서드가 있는지 확인합니다. __index 메서드가 함수라면, 그 함수를 호출합니다.

  • 메타테이블 메서드에서 "key" 매개변수가 전달되었는지 확인합니다2" 키 매개변수(mytable.key2이미 설정된 경우),"key" 매개변수를 전달하면2" 매개변수가 반환되면 "metatablevalue"을 반환하며, 그렇지 않으면 mytable에 해당하는 키 값을 반환합니다.

위 코드를 간단히 작성할 수 있습니다:

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

정리

Lua가 테이블 요소를 찾을 때의 규칙은 다음과 같습니다: 3 단계:

  • 1.테이블에서 검색합니다. 찾으면 해당 요소를 반환하고, 찾지 못하면 계속 진행합니다

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

  • 3.메타테이블에 __index 메서드가 있는지 확인합니다. __index 메서드가 nil이면 nil을 반환합니다. __index 메서드가 테이블이면, 반복하여 1、2、3;__index 메서드가 함수라면, 그 함수의 반환 값을 반환합니다。

이 부분 내용은 저자 환자에서 가져옵니다: https://blog.csdn.net/xocoder/article/details/9028347

__newindex 메서드

__newindex 메서드는 테이블 업데이트에 사용되며, __index는 테이블 접근에 사용됩니다.

테이블의 누락된 인덱스에 값을 할당하면, 해석자는 __newindex 메서드를 찾습니다: 존재하면 이 함수를 호출하여 할당 작업을 수행하지 않습니다.

다음 예제에서는 __newindex 메서드의 사용을 설명합니다:

mymetatable = {}
mytable = setmetatable({key1 = "value1"}
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 = "새 값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을 Lua에서5.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을 Lua에서5.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 코드를 작성할 수 있습니다.