함수는 루아에서 문장과 수식을 추상화 하기 위한 중요 기능 중 하나다.
함수는 특정 작업 또는 서브루틴을 수행하는 역활과 값을 계산하고 반환하는 함수 역할을 모두 담당한다.
전자일 경우에는 함수 호출을 문장처럼 사용하며, 후자일 때는 수식처럼 사용한다. 
print "Hello World"  --> print("Hello World") --같은 문장이다.
dofile 'a.lua'  --> dofile( 'a.lua') -- 같은문장이다.
print[[a multi-line message]]  --> print([[a multi-line message]]) -- 같은..
f{x=10, y=20} --> f({x=10, y=20}) -- 같다..
type{} --> type({}) -- 같은..
위는 그냥 봐도 함수를 사용할때 쓸수 있는 문법들이다.

루아에서는 신기한 연산자를 제공하는데 ':'콜론 연사자다
콜론 연산자는 o:foo(x)이렇게 사용하는데 같은 수식으로는 o.foo(o,x)이렇게도 사용할수 있다.
function add (a)
   local sum = 0
   for i,v in ipairs(a) do
      sum = sum + v
   end
   return sum
end
-- 기본적인 함수 정의다. 
-- 함수 정의 이름, 매개변수 목록, 함수 본문(리턴 값은 따로 정의 하지 않는다.)

function incCount(n)
   n = n or 1
   count = count + n
end
-- 루아에서는 매개변수를 체우지 않고 함수를 호출해도 오류 없이 작동한다.
-- 그렇게 때문에 함수를 정의 혹은 사용시 조심해야할것 같다.
-- incCount()이렇게 함수를 호출하면
-- n은 nil로 초기화 되고 n or 1에서 n은 nil이기 때문에 n = 1이 된다.

 1. 다중반환

루아의 특이하고 매우 편리한 기능 중 하나는 함수가 값을 여러개 반환할 수 있다는 것인데
미리 정의된 함수들은 대부분 값을 여러개 반환한다.
그 예로 문자열에서 패턴을 찾는 string.find 함수를 들수 있는데 이 함수는 패턴을 발견하면 색인을 두개 반환한다.
하나는 패턴이 처음 발견된 곳에 대한 색인이며, 두번째는 그패턴이 끝나는 위치의 색인이다.
이때 다중 배정을 사용해야한다..
루아의 신기한 기능들..
s, e = string.find("hello Lua users", "Lua")
print(s, e)     --> 7  9

-- 다중 반환 함수 정의 예
function maximum (a)
   local mi = 1
   local m = a[mi]
   for i, val in ipairs(a) do
      if val > m then
         mi = i;  m = cal
      end
   end
   return m, mi
end

print(maximum({8,10,23,12,5}))     --> 23    3

-- 반환값에 대해 더 자세히~
function foo0() end                           -- 반환값이 없다.
function foo1() return "a" end            -- 1개 반환
function foo2() return "a", "b" end      -- 2개 반환

x, y = foo2()                    -- x="a", y="b"
x = foo2()                        -- x="a", "b"버림
x,y,z = 10, foo2()             -- x=10, y="a", z="b"

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

print(foo0())         --> (공백? nil?)
print(foo1())         --> a
print(foo2())         --> a  b
print(foo2(), 1)     --> a  1
print(foo2() .. "x") --> ax
--위 print함수 호출에서 중요한부부은 마지막 2줄인데
-- foo2()함수에서 리턴값이 1개로 조정되었음을 알수 있다.
-- print()함수 같은 경우는 몇개의 매개변수를 받을지 정해져있지 않기때문에 1개만을 리턴했다.

-- f(g())라고 작성하고 f가 인수를 일정한 개수만 받는다면 루아는 위에서 본 내용과 같이 g의 결과값 개수를
-- f의 매개변수 개수로 조정한다.


-- 테이블 생성자도 함수에서의 결과와 비슷하다
t = { foo0() }          -- t = {} (빈 테이블)
t = { foo1() }          -- t = { "a" }
t = { foo2() }          -- t = { "a", "b"}
t = { foo0(), foo2(), 4 }  -- t[1] = nil, t[2] = "a", t[3] = 4

--함수안에서 함수호출후 리턴값을 알아보자
function foo(i)
   if i == 0 then return foo0()
   elseif i == 1 then return foo1()
   elseif i == 2 then return foo2()
   end
end

print(foo(1))   --> a
print(foo(2))   --> a   b
print(foo(0))   --> 결과값 없음
print(foo(3))   --> 결과값 없음

-- 강제로 return값을 한개로만드는법
print((foo2()))  --> a (함수 호출을 소괄호로 묶어주면 값을 하나만 리턴한다)

-- 값을 여러개 반환 하는 특별한 함수중 하나는 unpack이다.
-- 이 함수는 배열을 인수로 받아서 배열의 색인 1부터 시작하는 모든 요소를 결과로 반환한다.
-- 예로
f = string.find
a = {"hello", "ll"}
print(f(unpack(a)))   -->3   4
-- print(f(a)) 이렇게 사용하면 오류가 난다.
-- print(f(unapck(a)))는 print(f(a[1], a[2]))와 같다.
-- 지금 같은경우는 인수가 2개라 이렇게 한다지만 인수가 더 많다면.. unpack은 소스를 줄여주는 강력한 기능이다.

-- unpack함수는 미리 구현이 된기능이지만 재귀 함수를 써서 비슷하게 구현할수 있다.
function unpack(t, i)
   i = i or 1
   if t[i] then
      return t[i], unpack(t, i+1)
   end
end
2. 가변 개수 인수
루아의 몇몇 함수는 가변 인수를 받는다. 예를 들면 print함수는 한개 또는 그이상 인수를 사용한다.
-- 간단한 예
function add (...)
   local s = 0
   for i, v in ipairs{...} do
      s = s +v
   end
   return s
end

print(add(3, 4, 10, 25, 12))    -->  54
-- add함수는 모든 인수를 더한값을 리턴하는 함수다.
-- 딱 보면 알겠지만 세개의 마침표를 테이블 같이 인식하는것 같다(테이블과 똑같이 사용할순 없겠지만..)
-- 위처럼 수집한 인수들을 함수의 vararg(variable arguments의 약자)로 부른다. v5.1에서 추가된 기능이다.
-- 아래와 같은 표현방법도 된다.
function foo(...)
  local a, b, c = ...
end

--갑자기 생각나서 테스트해본 소스다
function foo1(...)
	print("sdf : ", (...)) --> 1
	print("sdf : ", ...)   --> 1, 2, 3, 4, 5, 6
end
foo1(1,2,3,4,5,6)
-- 위에 ()소괄호를 사용하여 함수 리턴값을 1개로 설정하는 부분을 응용해본것이다.

-- 이런식의 정의도 가능하다
function fwrite(fmt, ...)
   return io.wirte(string.format(fmt, ...))
end
-- 당연한 얘기지만 앞에 있는 변수 부터 차례대로 값을 체운다.
fwrite()  -- fmt = nil
fwrite("a") -- fmt = "a", 가변인수없음
fwrite("%d%d", 4, 5) fmt = "%d%d", varargs = 4, 5
--위는 책에 나온 내용인데 c언어에서의 printf함수도 얼마든지 구현가능할것같다(C언어 함수면 그냥 쓰면 되겠지만..)

-- {...}수식을 이용해서 인수들을 모두 테이블로 함치면 되지만
-- 아주 드문 경우로 vararg 목록이 유효한 nil값을 가지고 있을때는 select함수를 사용할수 있다.
-- select함수 호출은 언제나 하나의 고정 인수(선택자)를 가지며
-- 그 뒤에 추가 가변개수 인수들이 올 수 있다.(정확히 이해 하지 못해 책의 내용을 그대로 옮긴다.)
-- 그렇지 않다면 선택자는 문자열 '#'이 되어야만 하는데, 이 경우 나머지 추가 인수의 총 개수를 반환한다
-- 다음 반복문은 select함수를 사용하여 함수의 모든 vararg인수를 나열하는 방법을 보여준다.
for i=1 select('#', ...) do
   local arg = select(i, ...) -- i번째 인수를 가져온다.
end

-- select를 이해하기 위해 몇가지 테스트 해봤다
function foo1(number, ...)
	for i=1, select(number, ...) do
		local arg = select(i, ...)
		print(arg)
	end
end
foo1(2,2,3,4,5,6)
-- 출력값 2 3 4

function foo1(start, number, ...)
	for i=start, select(number, ...) do
		local arg = select(i, ...)
		print(arg)
	end
end
foo1(1,3,3,4,5,6)
-- 출력값 3, 4, 5, 6, nil

function foo1(...)
	for i=1, select(3, ...) do
		local arg = select(i, ...)
		print(arg)
	end
end
foo1(2,2,3,4,5,6)
-- 출력값 2, 2, 3

-- 3가지를 테스트해본결과 위에 2개는 아마도 버그가 아닌가 싶다..(물론 버그가 아닐수도 있다. 어디까지나 나의 생각)
-- 위에 2가지를 가지고 몇가지를 테스트 해본결과 나름대로 규칙은 있으나
-- 사용하기는 무리가 있는거 같다. 가장 아래 있는 소스(vararg만을 사용한 것)만을 사용하는것이 안전할 것 같다.

3. 이름 있는 인수

루아의 매개변수 전달 방식은 위치를 기반으로 하여 행해진다. 함수가 호출되면 각 매개변수 위치에 따라 인수를 맞춘다.
말하자면 첫 번째 인수는 첫번째 매개변수에 할당하는 방식이다. 하지만 어떤 경우에는 인수 이름에 따라 지정하는 것이 유용한 경우가 있다. 예를 들어, 파일 이름을 변경하는 os.rename함수를 사용할때 첫번째가 변경할 파일이름인지 2번째가 변경할 파일이름인지 헷갈릴다. 그러므로 이름 있는 인수 두개를 받는 함수로 다시 정의 할수 있다.
--rename(old="temp.lua", new="temp1.lua") -- 오류..
function rename(arg)
	return os.rename(arg.old, arg.new)
end
rename{old="temp.lua", new="temp1.lua"} -- 함수 호출.. (신기할뿐이다.. 문법 자체는 C언어보다 어려운것 같다.)

------------------------이름 있는 옵션을 가진 함수---------------------------------
-- _Window가 실제 윈도우를 생성하는 함수라면
-- 이렇게 정의 할수 있다.
function Window(options)
--	모호한 선택사항들을 검사한다.
	if type(options.title) ~= "string" then
		error("no title")
	elseif type(options.width) ~= "number" then
		error("no width")
	elseif type(options.height) ~= "number" then
		error("no height")
	end
	
	-- 다른 모든 것은 옵션이다.
	_Window(options.title, 
			options.x or 0, -- 기본값
			options.y or 0, -- 기본값
			options.width, options.height,
			options.background or "white", -- 기본값
			options.border -- 기본값이 false(nil)
			)
end

w = Window{ x=0, y=0, width=300, height=200, 
		title="lua", background="blue", border=true}
----------------------------------------------------------------------------------



이번 함수부는 내용이 정말 많았다. 필요 없는건 과감하게 뺄탠데
딱히 뺄게 없었다.

루아의 함수는 조심해야 하는 부분이 많다.
컴파일중의 오류는 다른 언어들 보다 적게 나겠지만(여러 타입을 변수 하나로 사용할수 있으므로 )
런타임중의 버그는 다른 언어들 보다 많이 날 확률이 높아 보인다.
이것이 단점이라면 단점이고

장점은 스크립트 언어중 가볍다라는 말을 많이 들었고 (그래서 게임업계에서 사용하는것 같다)
일단 배울땐 어려울지 몰라도 배우고 나면 C언어보단 무겁겠지만 간단하게 짤수 있어 편할듯 보인다.
그리고 내가 느낀점으론 않될것 같던 기능들이 다 된다는거다. (이건 않되겠지 했던게 몇개 있었다.)

이번엔 설명을 소스에 주석으로 달았는데
4장을 작성할때 소스 설명 소스 설명 이런식으로 작성했더니 로딩하는 부분이 길었다.

루아를 설치 할때 같이 설치되는 SciTE라는 프로그램인데 괜찮은것 같아 스샷을 올려본다.




PS. 책을 보고 쓰긴 했지만 내 느낌이나 내가 이해한 부분등 개인적인 생각이 들어 있기 때문에 100%신뢰 할수 없다.
신고

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

[Lua] 1부(언어) 5장. 함수 개요  (3) 2011.07.25
[Lua] 1부(언어) 4장. 문장  (0) 2011.07.21
[Lua] 1부(언어) 3장. 수식  (0) 2011.07.17
[Lua] 1부(언어) 2장. 루아 타입과 값  (0) 2011.07.16
[Lua] 1부(언어) 1장. 시작하기  (0) 2011.07.16
  1. 개지민 2012.02.07 14:02 신고

    select 예제 3개 중 두번째 것 [출력값 3, 4, 5, 6] 라고 되어있는데 마지막에 nil이 빠져있는 것 같네요
    출력값 3, 4, 5, 6, nil

    • Ergate 2012.02.08 00:48 신고

      덕분에 포멧하면서 지워졌던 루아를 다시 설치했네요
      마지막에 nil이 들어가는게 맞네요
      지적 감사합니다. 수정했어요

    • Ergate 2012.02.08 00:51 신고

      추가로 버그 인가? 라고 썼던글 취소합니다.
      다시 보니 정말 명확하네여;

+ Recent posts