'Lua Script'에 해당되는 글 9건

  1. 2018.04.01 [Lua] 클래스
  2. 2018.03.28 [Lua] 메타테이블과 메타메서드
  3. 2018.03.28 [Lua] 반복자
  4. 2018.03.28 [Lua] 함수의 내부
  5. 2018.03.27 [Lua] 함수
  6. 2018.03.27 [Lua] 문장
  7. 2018.03.27 [Lua] 표현식
  8. 2018.03.27 [Lua] 타입과 값
  9. 2018.03.27 [Lua] 기본 문법

[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 홍성곤
,

[Lua] 함수의 내부

Lua Script 2018. 3. 28. 20:28

Introduction 
: 함수는 숫자나 문자열처럼 전역이나 지역변수에 또는 테이블에 넣을 수 있고, 함수에 인자로 전달이 가능하며, 다른 함수로부터 반환 받을 수도 있다. 

함수는 이름이 없다. 
??? 무슨말인가 하면, 우리가 알고 있는 함수의 이름은 사실 그 함수 주소값을 담고 있는 변수의 이름이다. 

a = {p = print}
a.p("Hello world") --> Hello World
print = math.sin   -- print는 이제 sin 함수를 참조
a.p(print(1))         --> 0.841470
sin = a.p            -- sin은 이제 print 함수를 참조
sin(10, 20)        --> 10 20

그렇다 사실
function foo (x) return 2*x end 문장은 
foo = function (x) return 2*x end 를 보기좋게 썻던 것 뿐이다. 


1. 클로저 
: 클로저는 익명함수 + 비지역 변수 이다. 
익명함수는 알겠는데 비지역 변수는 무엇? 

function newCounter()
    local i = 0
    
    return function ()  -- 익명 함수 
                    i = i + 1
                    return i
                end
end

c1 = newCounter()
print(c1()) --> 1 
print(c1()) --> 2

위 코드에서 c1이 참조하고 있는 익명함수가 호출되는 시점에는 newCounter() 호출이 끝난 상태이기 때문에 지역변수 i의 생명주기가 끝나 버린다. 그런데 어떻게 c1이 참조하고 있는 익명함수에서 지역변수 i를 사용할 수 있는거지?
그것은 블럭이라는 존재 때문이다. 블럭은 익명함수와 위 코드에서 지역변수 i의 값을 저장해서 가지고 있는 놈이라고 보면 된다. 
그럼 아래를 보자. 

c2 = newCounter()
print(c2()) --> 1
print(c1()) --> 3
print(c2()) --> 2 

c1과 c2는 서로 다른 블럭이기 때문에 위와같이 서로다른 인스턴스처럼 동작한다. 

사실 루아에서 우리가 함수라 부르며 호출하는 것들은 함수가 아니라 클로저 이다. 

클로저는 콜백 함수를 구현할 때도 유용하다. 예를들어, 계산기를 만들때 0~9 까지 유사한 버튼을 만들어야 하는데 이런 버튼을 다음과 같은 함수로 만들어 낼 수 있다. 

function digitButton (digit) 
    reutrn Button { label = tostring(digit),
                             action = function ()
                                               addToDisplay(digit)
                                           end
                            }
end

다음은 테이블에 함수를 저장하는 특별한 문법을 소개하겠다. 일반적인 값을 테이블에 넣을 때 사용하는 문법을 써도 되지만, 다음과 같이 사용해도 된다.

Lib = {} 
function Lib.foo (x, y) return x + y end 
function Lib.goo (x, y) return x - y end 




            

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

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

[Lua] 함수

Lua Script 2018. 3. 27. 21:31

Introduction
: 루아에서는 루아로 작성한 함수를 물론, C로 작성한(또는 호스트 애플리케이션에서 사용하는 다른 언어로 작성한) 함수도 쓸 수 있다. 실례로 루아 표준 라이브러리의 모든 함수는 C로 작성한 것이다. 

function 함수이름 (매개변수)
    몸체
end

매개변수의 개수와 호출시 전달되는 인자의 개수가 다르면 남는 인자는 버려지고 인자가 모자란 경우에는 해당 매개변수는 nil이 된다. 

1. 여러 값 반환
: lua의 함수는 여러값을 반환할 수 있다. 

function multiReturn(a) 
    return a, a+1
end

local b, c = multiReturn(1)
print(b c) --> 1 2

함수의 반환값과 반환값을 저장하려고 하는 변수의 갯수가 틀린 경우 동적으로 처리해 주는 부분에 대해서 알아 보겠다. 

function foo0() end
function foo1() return "a" end
function foo2() return "a", "b" end

x, y = foo0()  -- x = nil, y = nil
x, y = foo1()   -- x="a", y = nil
x, y, z = foo2() -- x="a", y="b", z=nil

함수 호출이 목록의 끝에 있지 않으면 하나의 값만 반환한다. 
x, y = foo2(), 20 -- x= "a", y = 20
print(foo2() .. "x") --> ax
t = {foo0(), foo2(), 4} -- t[1] = nil, t[2] = "a", t[3] = 4

테이블 생성자는 함수가 반환하는 값을 개수 조정 없이 모두 받아간다. 
t = { foo0() } -- t = {}
t = { foo1() } -- t = {"a"}
t = { foo2() } -- t = {"a", "b"}

다음과 같은 형태도 가능하다. 
function foo(i)
    if i == 0 then return foo0()
    elseif i == 1 then return foo1()
    elseif i == 2 then return foo2()
end

print(foo(0)) --> nil
print(foo(1)) --> a
print(foo(2)) --> a b
print(foo(3)) --> nil

반환값 중에서 딱 하나를 받으려면 다음처럼 ()로 감싸면 된다. 
print((foo0())) --> nil
print((foo1())) --> a
print((foo2())) --> a

* f(g()) 에서 함수 f의 인자 개수가 정해져 있다면, g가 반환하는 개수가 f에 맞게 맞춰진다. 

table.unpack은 다중 반환을 해주는 특수 함수다. 인자로 받은 배열의 원소를 index 1의 원소부터 모두 다중반환 한다. 
a, b = table.unpack({10, 20, 30}) -- a = 10, b = 20 이고 30은 버려짐

unpack을 이용하면 동적으로 어떤 함수든지 임의의 인자로 호출할 수 있다. 
f(table.unpack(a)) -- f는 함수이고 a는 가변 개수의 인자를 담고 있는 배열이다. 

다음과 같이 길이지정도 가능하다. 
print(table.unpack({ "Sun", "Mon", "Tue", "Wed" })) -> Mon Tue 


2. 가변 인자 함수 
: 루아 함수는 가변 인자를 받을 수 있다. 

function luaPrint(...)
    for index, value ipairs {...} do
        print(value)
    end
end

luaPrint(1, 2, 3) --> 1 2 3

가변 인자를 table.pack 으로 묶어서 테이블로 저장 가능하다. 

function a(...)
    local packedTable = table.pack(...)
end


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

[Lua] 반복자  (0) 2018.03.28
[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 문장

Lua Script 2018. 3. 27. 20:57

1. 제어 구조
1) 조건문

if  a < 0 then 
    a= 0
end

if a < 0 then
    a = 0
elseif a == 0 then
    a = 1
else 
    a = -1
end

위처럼 사용한다. 

2) while문

while true do
    print("while")
end 

3) repeat-until문 
: 몸체를 실행한 다음 조건을 검사하기 때문에, 몸체가 한 번은 꼭 실행된다. 

repeat 
    line = io.read()
until line ~= ""
print(line)

위 코드는 빈 줄이 아닌 첫 줄의 내용을 출력하는 것이다. 즉, until 조건을 만족할때까지 몸체 부분을 계속 수행한다.

4) for문
1- 수치 for문

for var = exp1, exp2, exp3 do
    <something>
end

위 코드는 var의 값을 exp1에서 시작해서 exp2까지 exp3만큼 증가시키면서 <something>을 실행한다. exp3은 생략 가능하다. 생략하는 경우에는 1씩 증가한다. 
제한 없이 반복하고 싶은 경우에는 math.huge 상수를 쓰면 된다. 

2- 일반 for 문

for key, value pairs(t) do 
    print(k, v)
end

위 코드는 테이블 t의 모든 key, value 값을 출력하는 것이다.





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

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
[Lua] 기본 문법  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 표현식

Lua Script 2018. 3. 27. 20:14

1. 비교 연산
: 일반적인 언어와 같다. <, >, <=, >=, ==, ~= 을 지원한다. 
테이블과 유저데이터의 동등성은 참조 값(주소 값)이 같은지로 판별한다. 


2. 논리 연산자
: and, or, not이 있다. 
- and : C의 && 
- or : C의 || 
- not : C의 !


3. 테이블 생성자
: 생성자는 테이블을 생성하고 초기화하는 표현식이다. 테이블 생성자는 아주 유용하고 다재다능하게 활용되는 루아의 독특한 기능이다. 

days = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" } 
print(days[4]) --> "Wednesday" 
-- 테이블의 index는 기본적으로 1부터 시작한다. 

a = { "x" = 10, "y" = 20 } 

위처럼 Dictionary와 비슷하게 사용할 수도 있다. 

polyline = {
                   color = "blue",
                   thickness = 2 ,
                   {x = 0, y = 0},   --polyline[1]
                   {x = -10, y = 0} --polyline[2]
                 }

print(polyline[2].x) --> -10

위처럼 배열형식과 Dictionary 형식을 함께 사용해서 초기화할 수 있으며 테이블안에 테이블을 넣는것도 가능하다. 

b = { x = 10, y = 45; "one", "two", "three" }

위와같이 "," 와 ";" 둘 모두 구분자로 사용 가능하다. 


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

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
[Lua] 기본 문법  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 타입과 값

Lua Script 2018. 3. 27. 18:46

Introduction
: 루아는 동적 타입 언어다. 루아에서는 모든 값마다 타입이 있으므로, 변수를 선언할 때 타입을 지정하지 않는다. 

8가지 기본타입이 있다. 
- nil, boolean, number, string, userdata, function, thread, table

print(type(print)) --> function

a = print 
-- 이렇게 써도 문제가 없다. function도 하나의 타입이기 때문이다. 


1. 강제 변환
: 루아는 실행 중에 숫자와 문자열 사이의 자동 변환을 지원한다. 

print("10" + 1) --> 11
print("10" + "1") --> 11

반대로 문자열이 필요한 곳에 실수 값이 있으면 실수 값을 문자열로 변환한다.

print(10 ... 20)  --> 1020
-- ".."은 루아에서 문자열을 이어붙이는 연산이다. 

10 == "10" 의 결과값은 거짓이다. 
명시적으로 문자열을 실수로 변환할 때는 tonumber 함수를 쓰면 된다. tonumber는 문자열이 실수를 표기하지 않는 경우 nil을 반환한다. 

실수를 문자열로 변환하려면 tostring 함수를 호출하거나 10 .. "" 이처럼 실수에 빈문자열을 연결하면 된다. 


2. 테이블
: 테이블 타입은 연관 배열의 구현체이다. 여기서 연관 배열은 배열의 인덱스로 실수 이외에도 nil을 제외한 나머지 모든 값을 쓸 수 있는 배열을 말한다. 

테이블은 루아에서 유일한 자료구조이다. 또한 패키지나 객체를 표현하기 위해 테이블을 사용하기도 한다.
예를들어, io.read를 쓸때는 "io 모듈의 read 함수" 라는 의미로 쓴다. 루아에서 이 표현식은 "io 테이블에서 read라는 문자열을 키로 사용해서 찾은 것"을 의미한다. 

루아의 테이블은 객체다. 즉, 프로그램은 테이블에 대한 참조나 포인터만 조작한다. 따라서, 의도하지 않게 객체의 사본이 만들어지거나 새 테이블이 생성되는 경우는 없다. 

a = {} -- 테이블을 생성하고 a로 해당 테이블 참조
k = "x"
a[k] = 10
a[20] = "great"
print(a["x"]) --> 10
k = 20 
print(a[k]) --> great

더이상 테이블을 참조하는 변수가 없어지면 루아의 가비지 컬렉터가 메모리를 회수해간다. 

print(a["y"]) --> nil

위처럼 초기화되지 않은 필드의 값은 nil이 된다. 역시 전역변수와 마찬가지로 필드에 nil을 대입하면 필드가 삭제된다. 

print(a.x) --> 10 
-- a["x"]와 같음

#a 로 a 테이블의 길이를 얻어올 수 있다. 그러나 의도와는 다른값이 나올 수 있다.
예를들어 처음에는 10개 짜리 길이의 테이블이었는데 도중에 5번째 인덱스에 nil을 집어넣어 테이블의 값을 지우고 다시 #a로 길이를 참조하면 그래도 10이다. 

for i, j in pairs(a) do

end

그러나 위와같이 사용하면 a의 5번째 인덱스는 건너뛰고 순회한다. 


3. 함수
: 루아의 함수는 변수에 저장할 수 있고 다른 함수의 인자로 넘길 수 있으며 함수를 반환 받을 수도 있다. 


4. 유저데이터
: 유저데이터 타입을 써서 임의의 C 데이터를 루아 변수에 저장할 수 있다. C로 작성된 응용 프로그램이나 라이브러리에서 정의한 새로운 타입을 표현하기 위해 유저데이터를 사용한다.


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

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 기본 문법  (0) 2018.03.27
Posted by 홍성곤
,

[Lua] 기본 문법

Lua Script 2018. 3. 27. 18:19

1. 청크
: 루아가 실행하는 코드조각
: 청크는 "Hello World" 처럼 한줄로 간단할 수도 있고, ".lua" 파일로 묶어서 파일단위로 청크가 되기도 한다. 

독립적인 인터프리터가 컴퓨터에 설치되어 있다면 커멘드창에서

$ lua 파일이름.lua

이런식으로 실행시킬수 있다. 

$ lua -i 파일이름.lua 

위 명령은 해당 파일에 있는 코드를 실행한 후 대화모드로 넘어간다.

그냥 lua만 입력하면 바로 대화모드로 들어간다. 여기서 lua 코드를 입력하면 바로 실행 결과값이 나타난다. 

대화 모드에서 다른 .lua 파일을 불러올 수 있다.

> dofile("파일이름.lua")
n = norm(1.0, 1.5)  -- 위에서 불러온 파일에 있는 함수 호출

* 대화 모드에서는 local을 이용해서 지역변수를 사용하지 말자. 대화 모드에서는 한줄 한줄이 각기 다른 청크이기 때문에 이전에 선언한 local 변수가 바로 다음줄에서 유효하지 않다. 


2. 주석
한줄 주석 : -- 나는 주석이다.

여러줄 주석 :
-- [[ 
나는 주석
]] -- 


3. 전역 변수 
: 전역 변수는 선언할 필요가 없다. 그냥 쓰면 된다. 선언되지 않은 변수를 사용하면 그냥 nil이 들어갈 뿐이다. 

print(b) --> nil
b = 10
print(b) --> 10 

b = nil 
-- 이렇게 변수에 nil을 대입하면 그 변수에서 사용했던 메모리를 재활용할 수 있게 된다. 



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

[Lua] 함수의 내부  (0) 2018.03.28
[Lua] 함수  (0) 2018.03.27
[Lua] 문장  (0) 2018.03.27
[Lua] 표현식  (0) 2018.03.27
[Lua] 타입과 값  (0) 2018.03.27
Posted by 홍성곤
,