GaGe

Lua_Study(6장) 본문

Study/Lua

Lua_Study(6장)

Sorrel 2022. 7. 11. 17:12

6장. 루아 함수의 내부

루아에서는 함수가 1급 값이므로 전역 변수 뿐만 아니라 지역변수나 테이블의 필드에도 함수를 담을 수 있다. 함수를 테이블의 필드로 사용하는 것은 모듈과 객체 지향 프로그래밍 같은 고급 활용에 아주 필수적인 요소이다.

 

클로저

함수 안에서 다른 함수를 정의할 때 바깥쪽 함수의 모든 지역 변수를 안쪽에 정의하는 함수에서 쓸 수 있다. 이런 기능을 정적 범위 지정이라고 한다. 

 

function newCounter()
	local i=0
    return function()
    	i=i+1
        return i
    end
end

이 때는 이미 변수 i의 유효 범위를 벗어난 상태이다. 그럼에도 불구하고 이런 코드가 제대로 동작하는데, 이는 루아에서 클로저라는 개념을 지원하기 때문이다.

클로저 간단 설명 : 함수와 그 함수에서 필요로하는 모든 비지역 변수를 포함한 것

newCounter 함수를 다시 호출하면, 새 지역변수 i를 다시 만들고 이를 새 클로저로 받아 쓰게 된다.

 

c1 = newCounter()
c2 = newcounter()

print(c1())   --> 1
print(c2())   --> 1
print(c1())   --> 2

c1과 c2는 같은 함수에 대한 다른 클로저가 되고, 각자 지역 변수 i로부터 만들어진 다른 인스턴스처럼 동작한다.

 

 

클로저의 쓸모

루아에서는 함수가 그냥 일반 변수에 저장되기 때문에 함수를 재정의하는 것은 쉽다.

do
	local oldSin = math.sin
    local k = math.pi/180
    math.sin = function(x)
    	return oldSin(x*k)
    end
end

이렇게 하면 새 함수만 접근할 수 있는 내부 변수에 원래 함수를 두게 된다. 이러한 기법을 활용해서 샌드박스라 부르는 안전한 환경을 만들 수 있다.

  • 샌드박스(Sandbox)는 외부로부터 들어온 프로그램이 보호된 영역에서 동작해 시스템이 부정하게 조작되는 것을 막는 보안 형태이다.

원래의 제한이 없던 함수는 클로저의 내부 변수에 저장되어, 외부에서는 접근이 불가능해진다.

 

 

비전역함수

루아는 함수를 테이블의 필드나 지역 변수에도 저장할 수 있다.

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

print(Lib.foo(2,3), Lib.goo(2,3))  --> 5    -1


-- 테이블 생성자에서도 가능하다
Lib={
	foo = function(x, y) return x+y end
    goo = function(x, y) return x-y end
}


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

 

 

함수를 지역변수에 저장하면 그 함수는 지역함수가 되어서 해당 범위 안에서만 함수를 사용할 수 있다.

이는 패키징에 용이하게 해준다. 

local function f (params)
	body
end

 

 

재귀함수를 지역함수로 호출할 때는 문제가 생긴다.

local fact = function(n)
	if n == 0 then return 1
    else return n*fact(n-1)     --> 버그
    end
end

fact라는 지역함수가 아직 정의가 완벽하게 되지 않았으므로 fact 함수가 아닌 전역변수 fact를 참조하는 것을 의미하게 된다.

아래처럼 고치면 된다.

local fact
fact = function(n)
	if n == 0 then return 1
    else return n*fact(n-1)
    end
end

이렇게 되면 함수 안의 fact는 지역변수 local fact를 참조하게 된다. 

이 기능은 단축 문법을 사용해서 신경쓰지않도록 하자.

local function foo(params) body end

local function fact(n) if n==0 then return 1 else return n*fact(n-1) end end

 

꼬리호출

function f(x) return g(x) end

위 코드에서 g를 호출하는 것을 꼬리 호출이라고 한다. 이러한 경우에 꼬리 호출을 하고 f함수로 돌아올 필요가 없기 때문에 꼬리 호출을 위해 추가 스택을 사용하지 않는다. g를 실행하고 g를 호출한 f의 시점으로 다시 돌아오지 않는다. 이런 구현을 꼬리 호출 제거라고 한다.

 

아래 코드들은 꼬리 호출이 아니다.

return g(x) + 1
return x or g(x)
return (g(x))

하지만 args 부분은 복잡해도 된다. 왜냐면 함수 호출 전에 다 계산될 수 있기 때문이다. 아래 코드는 꼬리 호출이 될 수 있다.

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

 

'Study > Lua' 카테고리의 다른 글

Lua_Study(dofile vs loadfile vs load)  (0) 2022.07.12
Lua_Study(pairs와 ipairs의 차이점)  (0) 2022.07.12
Lua_Study(5장)  (0) 2022.07.11
Lua_Study(4장)  (0) 2022.07.11
LUA_study(1~3장)  (0) 2022.07.11
Comments