2015년 4월 20일 월요일

[루아] 변수의 참조에 대해서

루아의 변수는 다음과 같이 8가지 데이터타입을 갖는다.

nil
부울값
숫자
문자열
테이블
함수
----------
코루틴
유저데이터

이 중에서 코루틴과 유저데이터는 코로나SDK와 별로 관련이 없으므로 제외하고 나머지 여섯 개 중 참조를 갖는 것은 함수와 테이블이다. 문자열은 참조가 아니라는 것에 유의해야 한다. 참조란 실제 데이터가 저장된 곳을 가리키는 주소이다. (사실 참조도 주소'값'이므로 루아는 다 값이라고 주장하기도 하지만 혼동의 여지가 있으므로 여기서는 참조와 값을 구분하도록 하겠다.)
따라서 만약 어떤 테이블을 함수의 입력파라메터로 넘길 때 참조가 넘어가므로 그것을 받아서 조작하면 원래의 테이블값도 바뀌게 된다.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
local function Func1(a, tA)
a = 20


tA.x = 20
end

local function Func2(tA)
tA = {x=30}
end

local x, tX = 10 , {x=10}
Func1(x, tX) -- x는 값이, tX는 참조가 넘어간다.
print(x, tX.x) -- 10, 20 이 찍힌다.
Func2(tX)
print(tX.x) --20이 찍힌다.
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

위의 예에서 Func1()을 호출할 때 로컬 변수 x는 값이 넘어가고 테이블 tX는 참조가 넘어가므로 함수 호출 후 테이블의 필드만 값이 변경되었다. 반면에 Func2()내에서는 tA가 tX의 참조를 받기는 하지만 새로운 테이블의 참조로 초기화되었다. 따라서 이경우 tA는 넘어온 tX와는 전혀 별개의 것이 되어서 tX에는 아무런 영향을 끼치지 못한다. 그래서 맨 마지막 print()에서 20이 찍히는 것이다.

이와 마찬가지로 함수의 리턴값이 테이블이나 함수일 경우도 참조를 반환한다. 다음 예는 처음에는 조금 이해하기 힘들 수 있으나 한 번 살펴보도록 하겠다.

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
local function Func1(xR)
local x = xR or 0 -- 내부변수 x를 넘어온 값으로 초기화
local y = 0
local Func = function() print("x :"..x);end -- 여기서 x가 사용되었다.
return y, Func -- y는 '값'을, Func는 '참조'를 반환
end

local y1, Func2 = Func1(10)
local y2, Func3 = Func1(20)
Func3() -- 20 이 찍힌다.
Func2() -- 10 이 찍힌다.
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

함수 Func1()은 그 안에서 내부변수 y의 '값'과 내부변수 Func의 '참조'를 반환한다. 기본적으로 어떤 함수의 지역변수는 함수가 종료되면 사라진다는 것은 알고 있을 것이다. 변수 y는 함수가 종료되면 더 이상 참조되는 곳이 없으므로 GC의 타겟이 되어 사라질 것이다. 하지만 내부함수 Func는 그 참조가 호출된 곳의 Func2 변수를 통해서 계속 사용되므로 사라지지 않고 살아남게 된다. 그럼 내부변수 x는 어떨까? x변수는 Func안의 print()문에서 참조를 하고 있으므로 Func가 살아있는 한 x도 계속 살아있게 된다.
또 한가지 주의할 것은 Func2 와 Func3에서 참조하는 변수 x는 서로 별개의 것이라는 점이다. 따라서 처음 Func3()호출에는 20이 찍히고 맨 마지막 Func2() 함수를 호출하면 10이 찍힌다. 서로 '다른' 변수 x를 참조하고 있기 때문이다.

이것을 이해했다면 클래스를 구현할 때 외부에서는 접근할 수 없고 내부에서만 사용할 수 있는 private 변수/함수 를 구현하는데 응용할 수 있다.

댓글 없음:

댓글 쓰기