《 Programming in Lua 3 》 독서 노트 (10)

이 부분 은 매우 중요 할 것 이다. Lua 에서 유일한 데이터 구 조 는 바로 table 이 고 거의 모든 데이터 조작 은 table 을 바탕 으로 진행 된다.본 고 에서 언급 한 원 표 와 원 방법 은 바로 table 이 더욱 강력 한 기능 을 실현 하도록 도와 디자인 한 것 이다.
날짜: 2014.7.11
Part Ⅱ 
Metatables and Metamethods
Lua 에 서 는 table 을 직접 더하기, 비교 등 으로 조작 할 수 없습니다.메타 테이블 (Metatables) 을 사용 하지 않 는 한.메타 표 는 두 table 의 직접적인 추가 작업 을 정의 하 는 등 요 소 를 변경 할 수 있 습 니 다.Lua 는 두 table 의 추가 작업 을 처리 할 때 먼저 두 table 에 메타 표 가 있 는 지, 메타 표 에 가 있 는 지 확인 합 니 다.add 메타 방법 필드, 이 필드 가 있 으 면 lua 는 이 필드 에서 정 의 된 작업 에 따라 두 table 의 추가 작업 을 수행 합 니 다.
Lua 의 각 유사 한 변 수 는 연 결 된 메타 표 가 있 습 니까?(도대체 있 습 니까?) 그리고 table 과 userdata 는 각각 독립 된 메타 표를 가지 고 있 습 니 다.기본적으로 새로 만 든 table 은 메타 테이블 이 없습니다.
e.g.
t = {}
print(getmetatable(t))          --nil

이때 우 리 는 setmetatable 방법 을 통 해 원 표를 설정 할 수 있 습 니 다. 원 표 는 사실 하나의 table 에 해당 합 니 다.
e.g.
t1 = {}
setmetatable(t,t1)
print(getmetatable(t) == t1)          --ture

물론 lua 에서 저 희 는 table 에 대해 서 만 setmetatable 작업 을 수행 할 수 있 습 니 다. 다른 유형의 변 수 를 실행 하려 면 C 코드 를 사용 해 야 합 니 다.책 에 있 는 string 라 이브 러 리 는 string 형식 변수 에 메타 표를 설정 하 는 작업 과 관련 이 있 습 니 다.나머지 유형의 변 수 는 기본적으로 메타 표 가 없 습 니까?
print(getmetatable("ss"))               -- table
print(getmetatable(10))                  --nil

Arithmetic Metamethods
산술 연산 원 방법
여기 서 메타 표 의 사용 을 소개 합 니 다. 여기 서 하나의 table 로 set 를 표시 합 니 다. 우 리 는 연산 set 의 집합 등 조작 이 필요 합 니 다.
e.g.
Set = {} 
local mt = {}          --metatable for sets

function Set.new(l)     --    set,          
     local set = {}     
     setmetatable(set,mt)
     for _ v in ipairs(l) do set[v] = true end
     return set
end

이렇게 하면 매번 우리 가 set 를 새로 만 들 때마다 같은 메타 테이블 이 있 습 니 다.
s1 = Set.new{10,20,11,13}
s2 = Set.new{30,1}
print(getmetatable(s1))          --table: 0x7fa1eb4093a0
print(getmetatable(s2))          --table: 0x7fa1eb4093a0

원 표 에 원 을 추가 하 는 방법:add 필드 는 table 이 추가 작업 을 수행 하면
mt.__add = Set.union     --이때 의 에 주의 하 세 요.dd 필드 는 사용 할 수 없습니다. Set. union 이 아직 정의 되 지 않 았 기 때문에 이 코드 도 Set. union 을 정의 한 후에 두 어야 합 니 다. 그렇지 않 으 면 오 류 를 보고 할 수 있 습 니 다.정확 한 용법 은 먼저 정 의 를 내 린 다음 에 값 을 부여 하 는 것 이다.
-- 만약 mt.add = Set.union
--  Set.union
function Set.union( a,b )
     local res = Set.new{}
     for k in pairs(a) do
          res[k] = true
     end
     for k in pairs(b) do
          res[k] = true
     end

     return res
end

-- 이때
s3 = s1 + s2 잘못 보고    --attempt to perform arithmetic on global 's1' (a table value)
-- mt. 를add = Set. union 은 Set. union 을 정의 한 다음 에 놓 습 니 다.
마찬가지 로 설정mul 원 방법 도 비슷 한 요구 입 니 다.
--   Set.intersection
function Set.intersection( a,b )
     local res = Set.new{}
     for k in pairs(a) do
          res[k] = b[k]
     end
     return res
end
mt.__mul = Set.intersection

모든 산술 연산 원 방법:
__add (더하기),mul (곱 하기),sub (빼 기),div (제외),unm (마이너스),mod (모델 링),pow (멱),concat (연결)
Lua 는 두 변수의 산술 연산 을 처리 할 때 서로 다른 유형의 변 수 를 대상 으로 합 니 다. 예 를 들 어
e.g.
s = Set.new {1,2,3}
s = s + 8
이 때 실행 하면 오류 가 발생 합 니 다.
--bad argument #1 to 'pairs' (table expected, got number) 
두 변 수 를 처리 하 는 산술 연산 은 첫 번 째 변수 가 원 방 법칙 이 첫 번 째 변 수 를 사용 하 는 원 방법 을 정의 하면 두 번 째 요소 의 원 방법 을 고려 하지 않 는 다.첫 번 째 는 없고 두 번 째 는 있 으 면 두 번 째 것 을 사용한다.그렇지 않 으 면 잘못 보고 할 것 이다.
따라서 더 좋 은 제어 프로그램 이 실행 되 기 위해 서 는 두 변 수 를 같은 유형 으로 같은 메타 표를 가지 도록 제한 해 야 합 니 다.add 를 예 로 들 면 다음 과 같이 조작 할 수 있 습 니 다.
function Set.union( a,b )
     if getmetatable(a) ~= mt or getmetatable(b) ~= mt then
          error("xxx",2)     --        2,                
     local res = Set.new{}
     for k in pairs(a) do
          res[k] = true
     end
     for k in pairs(b) do
          res[k] = true
     end

     return res
end

Relational Metamethods
관계 연산 원 방법
Lua 의 관계 연산 원 방법 은 주로 다음 과 같다.
__eq (대등),lt (이하),le.다른 관계 조작 부호 에 대해 Lua 는 직접 전환 을 했다. a ~ = b 는 not (a = = b), a > b 는 b < a, a > = b 는 b < = a 에 해당 한다.
관계 연산 원 방법의 구체 적 인 사용 은 앞에서 언급 한 산술 연산 원 방법 과 유사 하 다.
주의해 야 할 것 은 두 변수의 메타 방법 이 다 를 때 같은 관계 연산 을 실행 하면 false 로 돌아 갑 니 다.
Library-Defined Metamethods
라 이브 러 리 정의 메타 방법
__tostring 메타 방법 
tostring 함 수 를 호출 할 때 함 수 는 먼저 변수 가 있 는 지 찾 습 니 다.tostring 메타 방법:
위 글 과 같이:
s = Set.new{1,2,2}
print(s)                                --table: 0x7f8349403d30
print(getmetatable(s))           --table: 0x7f8349409fd0

이 때 인쇄 된 것 은 그 값 이 아니 지만, 원 표 도 아니다
--to string
function Set.tostring( set )
     local l = {}
     for e in pairs(set) do
          l[#l + 1] = e
     end
     return "{" .. table.concat(l," , ") .. "}"
end
mt.__tostring = Set.tostring
print(s)                                   --{1,2,2}

메타 방법 을 설정 한 후에 야 값 을 정확하게 출력 할 수 있 습 니 다.
물론 우 리 는 일정한 방법 을 통 해 우리 의 원 표를 보호 할 수 있다. setmetatabe 와 getmetatable 도 원 방법 을 사용 했다. 우 리 는 이러한 특성 에 따라 우리 의 목적 을 달성 할 수 있다.
e.g.
mt.__metatable = "cannot change"
s1 = Set.new{}
print(getmetatable(s1))          --cannot change

우리 가 원 표를 바 꾸 고 싶 을 때
e.g.
setmetatable(s1,mt)               --error:cannot change a protected metatable 

잘못 보고 할 수 있 으 니, 그 원 표를 수정 해 서 는 안 된다. 이렇게 하면 우리 가 원 표를 보호 해 야 하 는 목적 을 달성 할 수 있다.
Table-Access Metamethods
Lua 는 메타 테이블 을 통 해 table 에 존재 하지 않 는 요 소 를 수정 하고 접근 하 는 행 위 를 제어 할 수 있 습 니 다.
The __index metamehod
우리 가 table 에 존재 하지 않 는 요 소 를 방문 하려 고 할 때, 우리 가 얻 은 값 은 nil 입 니 다.이것 은 일반적인 의미 에서 이 루어 진 것 입 니 다. 사실은 우리 가 table 에 존재 하지 않 는 요 소 를 방문 할 때 컴 파일 러 가 을 찾 도록 촉발 합 니 다.index 메타 방법, 이 방법 이 없 을 때 nil 로 돌아 갑 니 다.이 메타 방법 이 정의 되면 이 방법 이 정의 한 동작 을 되 돌려 줍 니 다.
이 특성 은 우리 가 계승 체 제 를 사용 하고 기본 변 수 를 계승 할 때 큰 도움 이 되 며 책 에서 도 이 를 예 로 들 어 설명 했다.
-- 기본 변수 가 있 는 table
prototype = {x = 0,y = 0,width = 100,height = 100}
원 표
mt = {}
구조 함수
function new(o)
     setmetatable(o,mt)
     return o
end

-- 메타 법 정의
mt.__index = function(_,key)
     return prototype[key]
end

-- 새로운 table 을 만 들 고 계승 체 제 를 사용 하려 면 기본 변수의 table 이 필요 하 다.
w = new{x = 10,y = 20}
print(w.width)           --100

이때 w 는 prototype 의 값 을 사 용 했 습 니 다.
__index 메타 방법 은 함수 가 필요 하지 않 고 table 일 수도 있 습 니 다.이 방법 이 함수 일 때 Lua 는 함수 에서 정 의 된 동작 을 수행 합 니 다. table 일 때 Lua 는 table 에서 직접 접근 동작 을 수행 합 니 다.
함수. rawget(t,i) table 각 요 소 를 방문 할 때 호출 하지 않 을 수 있 습 니 다index 작업. t 에 raw 접근 을 실행 합 니까?무슨 뜻
The __newindex metamethod
이 메타 방법의 역할 은 table 의 요소 값 을 업데이트 할 때 나타 납 니 다.우리 가 table 에 존재 하지 않 는 키 에 값 을 부여 하려 고 할 때 컴 파 일 러 는new index 의 메타 방법 은 컴 파일 러 가 있 으 면 이 방법 이 정의 한 작업 을 수행 합 니 다. 그렇지 않 으 면 직접 값 을 부여 합 니 다.여기 도 함수 가 하나 있어 요. rawset (t, k, v), 이 함 수 는 원 을 돌 리 는 방법 으로 t 에서 키 k 에 값 v 를 직접 설정 합 니 다.
책 에서 언급 한 효과 적 인 결합index 와new index 두 메타 방법의 사용 은 읽 기 전용 table, 기본 값 을 가 진 table 등 강력 한 디자인 기 교 를 가 져 올 것 입 니 다.
Tables with default values
table 은 기본 값 을 가지 고 있 습 니 다. 그 원 리 는 우리 가 table 에 존재 하지 않 거나 할당 되 지 않 은 키 를 방문 할 때 반환 값 은 고정 값 입 니 다. 여기 서 와 관련 됩 니 다.index 메타 방법
e.g.
function setDefault( t,d )
     local mt = {__index = function ( ... )
          return d
     end}
     setmetatable(t,mt)
end
tab = {x = 10,y = 20}
print(tab.x,tab.z)               --10,nil
setDefault(tab,0)
print(tab.x,tab.z)               --10,0

이 동작 은 tab 에 기본 값 0 을 설정 합 니 다. tab 에 정의 되 지 않 거나 존재 하지 않 는 요소 에 접근 하려 면 반환 값 은 0 입 니 다.
여러 개의 다른 table 에 여러 개의 기본 값 을 설정 하 는 작업 을 수행 합 니 다.
e.g.
local mt = {__index = function (t) return t.___ end}
function setDefault (t,d)
     t.___ = d
     setmetatable(t,mt)
end

이 기술 은 메타 표 의 정 의 는 함수 의 외부 에 있 고 기본 값 을 기본 값 으로 설정 할 table 자체 의 내부 에 저장 하 는 것 입 니 다.
이름 충돌 방지 동작:
e.g.
local key = {}          --   table  key
local mt = {__index = function (t) return t[key] end}
function setDefault (t,d)
     t[key] = d
     setmetatable(t,mt)
end

Tracking table accesses
유효한 사용index 와new index 는 table 에 대한 접근 과 할당 작업 을 감시 하 는 데 도움 을 줄 수 있 습 니 다.proxy (에이전트) 를 사용 하면 table 에 대한 모든 접근 동작 을 추적 하고 접근 하 는 값 을 추적 할 수 있 습 니 다.책 에서 언급 한 것 은 table 이 비어 있 을 때 만 모든 접근 동작 을 포착 할 수 있 습 니 다. 왜 요?
t = {}     --original table
--keep a private access to the original table
local _t = t
--create proxy   
t = {}
--create metatable
local mt = {
     __index = function ( t,k )
          print("*access to element " .. tostring(k))
          return _t[k]
     end,

     __newindex = function ( t,k,v )
          print("*update of element " .. tostring(k) .. " to " .. tostring(v))
          _t[k] = v      --update original table
     end
}
setmetatable(t,mt)

t[2] = "hello"
print(t[2])

인쇄 되 어 있 으 며, table 이 할당 에서 접근 하 는 과정 을 추적 하 였 습 니 다.
*update of element 2 to hello
*access to element 2
hello

Read-only tables
읽 기 전용 테이블
table 만 읽 는 원 리 는 주로 table 에 값 을 부여 하려 고 할 때 제한 하 는 것 입 니 다. 여 기 는new index 메타 방법의 사용.
e.g.
--read only table
function readOnly (t)
     local proxy = {}
     local mt = {
          __index = t,
          __newindex = function (t,k,v)
               error("attempt to update a read-only table",2)      --              ,    2,                   。
          end
     }
     setmetatable(proxy,mt)
     return proxy
end
을 통 해new index 메타 방법 에서 적당 한 수정 을 하면 우리 의 table 을 읽 기 전용 table 로 바 꿀 수 있 습 니 다.

좋은 웹페이지 즐겨찾기