사용자 도구

사이트 도구


kb:luaglossary

Lua Glossary

루아에서 나오는 여러 가지 용어들에 대해 정리해보고자 한다. C랑 루아랑 서로 호출하는 것도 좋지만, 루아라는 언어 자체에 대해서 좀 심도있게 알지 못하니, 바깥쪽만 맴도는 느낌이 들었기 때문이다.

First-class function

함수를 변수에다 할당하거나, 리턴값으로 반환할 수 있는 기능을 말한다. 루아에서는 함수가 명시적인 이름을 가지지 않고, 오로지 변수를 통해 액세스된다. 그러므로 이름을 바꾸거나, 삭제가 가능하다. 이를 이용해 HigherOrderFunction이나 FunctorObject를 간단하게 구현할 수 있다.

function new_function()
    return function(msg)
        print(msg .. " World")
    end
end
 
a = print
a("Hello")
a = new_function()
a("Hello")

Lexical Scoping, Upvalue or External Local Variable, Closure

internal 함수가 external 함수 안에서 정의되었을 때, internal 함수는 external 함수의 지역 변수를 모두 액세스할 수 있다. 이런 기능을 lexical scoping이라고 한다. 루아는 lexical scoping을 지원한다. 다음과 같은 예를 생각해 보자.

function external()
    local i = 0
    return function() -- 무명(anonymous) 함수
        i = i + 1
        return i
    end
end

무명 함수 안에서 external 함수 안에 있는 i 변수를 액세스하고 있다. 이 경우 무명 함수에게 있어 i는 전역 변수도 아니고, 그렇다고 지역 변수도 아니다. 이런 변수를 external local variable, 또는 upvalue라고 부른다.

위의 예에서 계속해서, external 함수를 호출해 함수 객체를 변수에다 저장한 다음, 저장한 함수를 호출했다고 하자.

f = external()
f()

f 함수, 즉 무명 함수를 호출하는 시점에서 i 변수는 이미 존재하지 않는다. 그렇다면 무명 함수는 어디서 i 변수를 찾아야 하는가? 루아는 이를 처리하기 위해 closure라는 개념을 도입했다. 즉 closure란 함수 + 그 함수에서 액세스하는 external local variable들의 집합이다.

그렇다면 external 함수를 여러번 호출하면 어떻게 되는가?

f = external()
g = external()

f와 g는 서로 다른 closure, 즉 서로 다른 external local variable들을 가지게 된다. 즉 f 함수를 계속 호출해 i 값을 증가시키더라도, g 함수의 i 변수와는 아무런 상관이 없다는 말이다.

Tail Call or Proper Tail Recursion

테일 콜은 임의의 함수가 다른 함수(자기 자신도 포함)를 마지막 명령(instruction)으로 호출하는 경우를 말한다. 다음과 같은 경우에 테일 콜이 일어난다.

function f(x)
    return g(x)
end

f 함수가 g 함수를 호출한 후에는 더 이상 할 일이 없다. 즉 g 함수를 호출한 다음에 f 함수로 돌아올 필요가 없다는 말이다. 따라서 f 함수에 대한 정보를 스택에다 보관할 필요가 없다. 루아는 이 사실을 적용해서 테일 콜을 실행할 때는 스택 공간을 사용하지 않는다.

그렇다면 이것이 의미하는 바는 무엇인가? 바로 무한 재귀 호출이 가능하다는 말이다. 테일 콜만으로 이루어진 함수의 경우, 무한으로 재귀호출을 해도 스택 오버플로가 일어나지 않는다. 아래의 함수가 좋은 예이다.

function foo(n)
    if n > 0 then return foo(n-1) end
end

마지막 함수를 호출한 다음 할 일이 아무 것도 없어야 테일 콜이라는 것을 상기하면 다음의 구문들이 테일 콜이 아니라는 것을 알 수 있을 것이다.

return g(x) + 1  -- 더하기 연산을 해야 한다.
return x or g(x) -- 둘 중에 하나를 골라 리턴해야 한다.
return (g(x))    -- g(x)의 반환값 중에 하나를 골라 리턴해야 한다.

즉 루아에서 테일 콜 구문의 포맷은 반드시 return g(인수들...)의 형태를 가져야한다. 그러므로 다음과 같은 구문은 테일 콜이다.

return x[i].foo(x[j] + a*b, i+j)

Coroutine

스레드와 비슷한 개념이다. 각각 자신만의 스택과 지역 변수 등을 가지고 있고, 전역 변수는 서로 공유한다. 단 스레드는 알다시피 동시에 실행되나, 코루틴은 그렇지 않다. 즉 한 순간에 하나의 코루틴만이 실행된다. 다른 코루틴을 실행하기 위해서는 현재 실행 중인 코루틴의 동작을 중지시켜야 한다. 또한 외부에서 코루틴의 동작을 중지시킬 수 없다. 비선점형(Non-preemptive)이라는 말이다.

Metatable & Metamethod

메타 테이블은 테이블의 동작을 정의하는 테이블이다.

굳이 비슷한 걸 들자면, C++의 오퍼레이터 오버로딩을 들 수 있겠다. C++에서 임의의 클래스를 만든 경우, 기본적으로 ”+” 연산이 불가능하다. 하지만 ”+” 연산자를 재정의해주면 가능해진다.

루아에서도 테이블의 경우, 기본적으로는 ”+” 연산이 불가능하다. 그러나 테이블에 임의의 메타 테이블을 설정하고, 그 메타 테이블에 "__add" 필드를 정의해 주면, ”+” 연산도 가능해진다. "__add" 필드에 들어가는 함수, 즉 메타 테이블에 들어가는 함수를 메타 메서드라고 부른다.

Protected mode

보호 모드가 아닐 경우, 루아 내부에서 문제가 생기면, 루아 컨텍스트 자체가 오염될 수 있다. 컨텍스트 자체가 오염된 경우, 더 이상 스크립트를 실행할 수 없다. 경우에 따라서는 호스트 프로그램이 종료될 때도 있다.

보호 모드에서는 문제가 생겨도, 컨텍스트가 오염되지 않는다.

C 함수는 논외다. 루아에서 호출한 C 함수 내부에서 에러가 발생한 경우, 루아가 할 수 있는 일은 거의 없기 때문이다.

Weak Table

테이블 안에 오브젝트(함수,테이블,풀 유저데이터)가 들어있는 경우, 그 오브젝트는 테이블이 사라지지 않는 이상, 기본적으로 가비지 컬렉션의 대상이 되지 않는다. 위크 테이블을 이용하면 테이블 안의 오브젝트도 가비지 컬렉션의 대상이 되도록 할 수 있다.

임의의 테이블을 위크 테이블로 만들기 위해서는 메타테이블과 __mode 메타메서드를 설정해줘야한다. 자세한 것은 LuaGarbageCollection 페이지에…


kb/luaglossary.txt · 마지막으로 수정됨: 2014/11/06 17:18 (바깥 편집)