포스트(post)

22. 함수 만들기 1 - (defun) 1

목표

함수를 만드는 함수인 (defun)을 알아봅시다



함수란

그동안 계속 써왔던 (getpoint), (if)와 같이, 무언가 기능을 실행하는 애들을 함수라고 합니다. 위 함수들은 오토리습에 이미 정의(등록) 되어 있는 기본함수들이었습니다.

하지만 자기가 원하는 함수를 직접 만들어 쓸 수도 있습니다. 함수를 정의(등록)할 때는 (defun)이라는 함수를 사용합니다. defundefine function(함수를 정의해라)의 줄임말입니다. (defun)함수는 아래와 같은 형식으로 사용합니다

1
2
3
(defun 함수명 (지역변수지정부)
	함수실행코드들
)

이렇게 정의(등록)한 함수는, 그동안 사용했던 것과 마찬가지로 (함수명 인자1 인자2 ...)형식으로 쓸 수 있습니다.



지역변수가 없는 함수

먼저 안녕하세요!만을 출력하는 간단한 함수를 하나 만들어 봅시다. 아래 코드를 복사해서 명령어 창에서 실행 해 주세요

1
2
3
4
(defun sayHi ()
	(princ "\n안녕하세요!")
	(princ)
)

함수명은 sayhi이며, 지역변수지정부에 아무것도 적지 않았으므로, 아무 인자도 받지 않는 함수입니다.

이제 도면에 (sayhi)라는 함수가 정의(등록)되었습니다. 아래와 같이 함수를 사용 해 봅시다.

1
(sayhi)



지역변수가 있는 함수

지역변수지정부

지역변수라는것은, 해당 함수가 시작될 때 생성되었다가 함수가 끝날때는 삭제되는 변수를 말합니다. 함수명 다음에 오는 (지역변수지정부)에서, 함수 내부에서만 사용 할 지역변수들을 지정할 수 있습니다.

지역변수에는 함수를 호출(사용)시에 꼭 넣어줘야 하는 인자도 포함됩니다.

(지역변수지정부)는 가운데 /를 기준으로, 왼쪽함수 호출(사용)시 넣어야 할 인자들을, 오른쪽그 외에 추가로 함수 내부에서만 사용할 변수들을 지정합니다. 여기서 주의 할 점은 /는 양 옆으로 어느것과도 붙어 있어서는 안 됩니다.

함수 외부에서 사용하지 않을 변수는 반드시 지역변수지정부에 추가시켜 줘야 합니다.(이유는 아래에 적겠습니다.)

예를 들어서 인자가 1개 필요하면서, 추가로 함수 내부에서만 사용할 변수가 2개인 함수는 아래와 같이 만들 수 있습니다.

1
2
3
(defun 함수명 (인자1 / 지역변수1 지역변수2)
	함수실행코드들
)

여기서 인자1/, 그리고 /지역변수1사이는 반드시 떨어져 있어야 합니다. 만약 붙어버린다면, 오토리습은 /를 인식하지 못하고, 인자1/ 또는 /지역변수1을 변수라고 생각합니다. 왜냐하면 오토리습에서는 변수명/를 포함한 특수문자를 사용할 수 있기 때문입니다. 그렇게 되면, 지역변수지정부/가 없으므로 오토리습은 이 함수를 3개의 인자를 가진 함수로 생각합니다.

1
2
3
(defun function1 (인자1 /지역변수1 지역변수2)) ; <- 함수호출시 인자 3개를 넣지 않으면 오류발생
	...
)

만약 함수 호출시 필요한 인자만 있고, 추가적인 지역변수가 없는 함수에서는 /를 생략해도 됩니다.(있어도 상관없습니다.)

1
2
3
(defun 함수명 (인자1)
	함수실행코드들
)

만약 함수 호출시 필요한 인자가 없고 지역변수들만 있는 함수에서는 /를 생략해서는 안 됩니다(생략하면 전부 인자들로 취급됩니다.)

1
2
3
(defun 함수명 ( / 지역변수1 지역변수2)
	함수실행코드들
)

인자가 없는 함수에서는 /과 그 앞의 ( 사이에 반드시 공백을 추가해야 합니다. 만약 (/처럼 붙어버리면, 오토리습은 앞에서 다루었던 나눗셈함수인 (/)로 생각하게 됩니다.

1
2
3
(defun function1 (/ 지역변수1 지역변수2)) ; <- 오류발생
	...
)

예시

정수 2개를 입력받아서 그 합을 보여주는 함수를 한번 만들어 봅시다. 아래 코드를 복사해서 명령어 창에서 실행 해 주세요

1
2
3
4
5
(defun add (num1 num2 / sum)
	(setq sum (+ num1 num2))
	(princ (strcat (itoa num1) " + " (itoa num2) " = " (itoa sum) "입니다."))
	(princ)
)
  • 이 함수의 지역변수지정부(num1 num2 / sum)이므로, 이 함수를 사용하려면 num1num2 에 해당하는 2개의 인자를 넣어야 하며, num1, num2, sum 세 변수는 이 함수가 종료되면 사라집니다.

다시한번 말씀드리지만, /는 양 옆 누구와도 붙어있어서는 안 됩니다.

이제 위 함수가 도면에 등록되었으니, 한번 사용 해 봅시다. 들어갈 숫자는 마음대로 변경해도 좋습니다.

1
(add 5 7)
  • 우리가 정의한 (add)함수는 num1num2 2개의 정수를 인자로 받는 함수입니다. 따라서 (add)함수를 사용할 때는 정수 2개를 인자로 넣어줘야 합니다.
  • 이렇게 호출한 (add)함수에서는 첫번째 위치한 num1에는 5가, 두번째 위치한 num2에는 7이 들어갑니다. 마치 (setq num1 5)(setq num2 7)을 한 것 처럼요. 그 후에는 (add)함수 내부의 코드들을 실행하게 됩니다.



지역변수를 선언해야 하는 이유

지역변수지정부로 굳이 해당함수 안에서만 사용 될 변수들을 지정 해 주는 이유가 뭘까요?

오토리습에서는 변수를 생성하면 기본적으로 다른 모든곳에서 그 변수를 사용할 수 있게됩니다. 이를 전역(Global)변수라고 합니다. 하지만, 만약 다른 함수에서 같은 변수명을 사용한다면 문제가 될 수 있습니다.

다음의 경우를 한번 봐 봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
(defun fun1 () ; 함수 fun1 정의
	(setq num 3) ;5
) ;6

(defun fun2 () ; 함수 fun2 정의
	(setq num 5) ;3
	(fun1) ;4
) ;7

(setq num 1);1
(fun2) ;2
(princ num) ;8
  • 먼저 (fun1)을 정의합니다. 이 함수에서는 num에 3을 넣는 내용이 들어있습니다.
  • 다음으로 (fun2)를 정의합니다. 이 함수에서는 먼저 num에 5를 넣은 후, (fun1)함수를 호출합니다. 여기까지는 함수 정의부입니다.
  • 이제부터는 실제로 코드를 실행합니다. num에 1을 넣습니다.
  • (fun2)함수를 호출합니다.
  • 마지막으로 num에 담긴 값을 출력합니다.

그렇다면 과연 명령어창에 출력되는 값은 5일까요? 3일까요? 정답은 3입니다. 왜냐하면 다음과 같이 실행되기 때문입니다.

  1. num에 1 넣음
  2. (fun2)호출
  3. num에 5 넣음
  4. (fun1) 호출
  5. num에 3 넣음
  6. (fun1)종료
  7. (fun2) 종료
  8. num의 값인 3 출력

이는 fun1fun2 모두 num이라는 동일한 이름의 변수를 사용하기 때문에 발생하는 문제입니다.

이를 방지하려면 함수 내부에서만 사용되었다 사라지는 지역변수를 만들어야겠죠. 전역이 아닌 함수 내부에서만 존재하기 때문에 지역(Local)변수라고 합니다.

이제 각 함수의 num변수를 지역변수로 등록 해 봅시다.

1
2
3
4
5
6
7
8
9
10
11
12
(defun fun1 ( / num) ; num을 지역변수로 등록
	(setq num 3) ;5
) ;6

(defun fun2 ( / num) ; num을 지역변수로 등록
	(setq num 5) ;3
	(fun1) ;4
) ;7

(setq num 1);1
(fun2) ;2
(princ num) ;8

이제는 다음과 같이 실행됩니다.

  1. num(전역변수)에 1 넣음
  2. (fun2)호출
  3. num((fun2)지역변수)에 5를 넣음
  4. (fun1) 호출
  5. num((fun1)지역변수)에 3을 넣음
  6. (fun1) 종료 : num((fun1)지역변수) 삭제
  7. (fun2) 종료 : num((fun2)지역변수) 삭제
  8. num(전역변수)의 값인 1 출력

(fun1)(fun2) 내부의 num은 지역변수로 지정했기 때문에, 각 함수가 종료될 때 지역변수인 num도 같이 삭제됩니다.

이렇게 외부 함수를 실행하더라도 각 변수는 해당 함수 내에서만 사용 가능하고, 함수 외부에서는 변경이 불가능하게 만들어야, 예상하지 못한 결과를 막을 수 있습니다.

따라서 일부러 전역변수로 만들고 싶은 경우가 아닌이상, 항상 함수를 정의(생성)할 때는 함수 내부에서 사용할 변수들은 지역변수지정부에 등록하여 사용해야 합니다.

© IsaacGo. All rights reserved.