[Lua] 클래스

Lua Script 2018. 4. 1. 15:13


Account = { balance = 0 }

function Account.withdraw(value)
    Account.balance = Account.balance - value
end

Account.withdraw(100.0)

위와 같이 테이블에 값을 저장하는 변수와 함수를 정의해서 객체처럼 정의할 수 있다. 

그런데 withdraw 함수를 보면 "Account." 으로 인해 Account 직접 명시해서 접근하고 있다. 객체지향적 관점(특히 상속)으로 보면 이것은 매우 안좋다. 
다른 언어에서 사용하듯이 lua도 "self" 식별자를 이용해서 자신을 가리키는 메커니즘을 제공한다. 

function Account.withdraw(value)
    self.balance = self.balance - value
end

그렇다면 위 정의가 instance를 생성하려면 어떻게 해야 하는가?
다른 언어에서도 구조가 비슷하듯이 Lua에서 클래스와 인스턴스, 상속등의 구조를 만드려면 metatable을 이용해야 한다. 

앞에 Chapter에서 말했듯이, table은 table마다 metatable을 다르게 가져갈 수 있다. 하지만 객체 지향 구조를 만들기 위해서는 instance가 되는 table들이 같은 Class table을 metatable로 가지도록 만들어야 한다. 

Account = { balance = 0 }

function Account:withdraw(value)
  self.balance = self.balance - value
end

function Account:new()
  local instance = { balance = 0 }

  setmetatable(instance, self)
  self.__index = self

  return instance
end

accountA = Account:new()
accountB = Account:new()

accountA:withdraw(30)
accountB:withdraw(20)

print(accountA.balance)
print(accountB.balance)

accountA:withdraw(20)

print(accountA.balance)


위 코드에서 accountA, accountB는 클래스 Account의 instance이고, metatable로 클래스 Account를 가지고 있다. 

accountA:withdraw(30)를 호출하면 accountA 테이블에는 withdraw 함수가 존재하지 않으니 metatable의 __index를 참조해서 withdraw 함수를 찾아서 호출한다. 

즉 getmetatable(accountA).__index.withdraw(30) 이 실행된다. 



'Lua Script' 카테고리의 다른 글

[Lua] 메타테이블과 메타메서드  (0) 2018.03.28
[Lua] 반복자  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
Posted by 홍성곤
,

Introduction 
: 루아의 모든 테이블과 유저데이터는 각각 메타테이블이 따로있고, 테이블과 유저데이터를 제외한 다른 타입은 하나의 메타테이블을 공유하는 형태다.

새로운 테이블을 생성하면 항상 메타테이블이 없이 생성된다. 

t = {}
print(getmetatable(t)) --> nil 

t1 = {} 
setmetatable(t, t1) 
print(getmetatable(t) == t1) --> true

루아 코드로는 테이블의 메타테이블만 설정할 수 있다. 다른 타입의 메타 테이블을 조작하려면 직접 C 코드를 이용해야 한다. 

그리고 테이블은 자신의 메타테이블이 될 수도 있는데, 이때는 자신의 행위를 기술하는 데 사용된다. 


1. 산술 메타메서드 
: 우선 Set을 구현해보자. 

Set = {} 

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

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

function Set.intersection (a, b) 
    local res = Set.new{}
    for k in pairs(a) do
        res[k] = b[k]
    end

    return res
end

이제 두 집합의 합집합을 구할 때 덧셈 연산자 "+"를 사용하려 한다. 이를 위해 Set을 표현하는 모든 테이블이 하나의 메타테이블을 공유하도록 할 것이다. 이 메타테이블에서 덧셈 연산자를 어떻게 처리해야 하는지 정의해야 한다. 

local mt = {} -- Set에 대한 메타테이블 

다음 단계로, Set을 생성하는 Set.new 함수를 수정한다. 

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

마지막으로 메타테이블에 덧셈을 처리하는 방법을 설멸하는 메타메서드인 "__add" 추가한다. 

mt.__add = Set.union

s1 = Set.new{10, 20, 30, 50}
s2 = Set.new{30, 1}
s3 = s1 + s2
Set.print(s3) --> {1, 10, 20, 30, 50}

동일한 방식으로 곱셈 연산자를 이용해서 교집합을 구하도록 할 수 있다. 

mt.__mul = Set.intersection
Set.print((s1 + s2) * s1) --> {10, 20, 30, 50} 

메타테이블에는 산술 연산자에 대응하는 필드명이 있다. __add, __mul 외에 뺄셈을 위한 __sub, 나눗셈을 위한 __div, 부정 연산을 위한 __unm, 나머지 연산을 위한 __mod, 지수 연산을 위한 __pow가 있다. 그리고 붙이기 연산을 위한 __concat 필드도 정의할 수 있다. 


2. 관계 메타메서드 
: 메타테이블을 이용하면 관계 연산자에도 의미를 부여할 수 있다. 
- __eq : = 
- __lt : < (작음)
- __le : <= (작거나 같음) 
위 세개의 관계 연산자는 별도의 메타메서드로 제공되지는 않는다. 대신 루아는 a~=b를 not(a==b)로 표현하듯이 not을 써서 표현할 수 있다. 


3. 라이브러리에 정의된 메타메서드
: print() 함수는 인자를 받으면 tostring을 호출해서 출력한다. 
위 Set에 다음과 같은 함수가 있다고 해보자. 

function Set.tostring (set)
    local whole = {}
    for e in pairs(set) do
        whole[#whole + 1] = e
    end
    
    return "{" .. table.concat(whole, ", ") .. "}"
end

위 함수가 있으면 해당 함수를 tostring에 대한 메타메서드로 지정할 수 있다. 

mt.__tostring = Set.tostring

s1 = Set.new{10, 4, 5}
print(s1) --> { 4, 5, 10 }


'Lua Script' 카테고리의 다른 글

[Lua] 클래스  (0) 2018.04.01
[Lua] 반복자  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 반복자

Lua Script 2018. 3. 28. 20:41

반복자
: 호출할 때마다 자료구조의 다음 원소를 반환하는 함수를 반복자라고 표현한다. 
우리는 지금부터 클로저를 사용해서 반복자 하나를 만들어 보겠다. 

function values (t)
    local i = 0
    return function ()
                   i = i + 1
                   return t[i]
                end
end

t = {10, 20, 30}
for element in values(t) do 
    print(element)
end

위 values 함수를 클로저를 생성하는 팩토리 함수이다.



'Lua Script' 카테고리의 다른 글

[Lua] 클래스  (0) 2018.04.01
[Lua] 메타테이블과 메타메서드  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
Posted by 홍성곤
,