본문 바로가기

General/LISP

[LISP #2] 리스프의 전역변수, 전역함수, 지역변수, 지역함수


2장에서는 전역변수 및 전역함수, 지역변수 및 지역함수, 기본적인 CLISP의 사용 등이 나옵니다.



리스프에서 전역변수

리스프에서 전역으로 선언된 변수를 최상위 정의(top-level definition)라 부릅니다. defparameter 함수를 이용해서 최상위 정의를 할 수 있습니다.

정의 - defparameter

기본형)

 > (defparameter *name* value)

defparameter의 첫번째 매개변수 : 새로운 변수 이름
defparameter의 두번째 매개변수 : 값 


새로운 변수의 이름은 * 기호로 감싸주어야합니다.(* 기호는 귀마개 라는 애칭... 을 쓴다고 하네요... 하...)
리스프는 * 기호를 변수 이름의 일부분으로 인식하고 이를 무시합니다. 리스퍼(리스프 프로그래머)는 관습처럼 전역변수에 * 기호를 붙여 지역변수와 구분하기 쉽게 해줍니다.
 
foo 라는 전역변수를 정의하며 17로 값을 지정해보았습니다.  REPL에 *FOO*가 출력됩니다.
defparameter는 최상의 정의 명령이 제대로 실행 된 경우, 정의 된 변수 이름을 REPL에 출력합니다.
 
최상위 정의의 예)

 > (defparameter *foo* 17)
 *FOO* 
 
REPL에 *foo*를 입력하면, 정의된 변수의 값이 출력됩니다.
 
전역변수 사용예) 

 > *foo*
 17 

이미 정의 된 변수 이름을 다시 defparameter로 정의하게 되면, 이전 값은 새로운 갚으로 덮어씌워집니다.
 
사용 예)

 > (defparameter *foo* 24)
 *FOO*

 > *foo*
 24 
  

값 변경 - setf


defparameter를 다시 호출해서 값을 새로운 갚으로 덮어씌우는 것 말고, 이미 정의 된 변수의 값을 변경하는 함수가 있습니다. 

기본형)
 > (setf *name* value)

setf의 첫번째 매개변수: 이미 정의된 전역변수의 이름
setf의 두번째 매개변수: 변경할 값(새로운 값)

setf 사용예)
 > (defparameter *bar* 17)
 *BAR*

 > *bar*
 17

 > (setf *bar* 24)
 24 



리스프에서의 전역함수

커먼 리스프(CLISP)에서 함수는 defun 명령으로 정의할 수 있습니다.

정의 - defun

기본형) 
 > (defun function-name (arguments)
    ......) 


첫 부분에는 함수의 이름과 매개변수를 정의하고, 그 다음으로 함수의 로직을 구성하는 코드를 작성합니다.

함수 선언 및 사용 예) 
 > (defparameter *foo* 10)
 *FOO*

 > (defparameter *bar* 5)
 *BAR*

 > (defun get-foo-plus-bar ()
        (+ *foo* *bar*) 
    ) 

 GET-FOO-PLUS-BAR

 > (get-foo-plus-bar)
 15 


LISP는 return 처럼 값을 반환하는 명령이 필요 없습니다. 함수 내부 코드에서 마지막으로 계산된 결과가 자동으로 반환되기 때문이죠.

위의 함수, get-foo-plus-bar를 보면, 매개변수는 없고, *foo*와 *bar*를 더해주는 코드만 있을 뿐입니다. LISP에서는 전위표기식 연산을 사용합니다. 이 함수에서는 마지막 계산된 결과가 *foo*(10) + *bar*(5), 즉 15이기때문에 15를 자동으로 반환하고, 이를 REPL이 출력해준것입니다.



리스프에서 지역변수

한 함수 내에서만 또는 코드의 일정 부분에서만 쓰도록 변수와 함수를 제한할 수 있습니다. 이를 지역변수와 지역함수라고 하는데, 여기서는 지역변수를 먼저 살펴봅니다.

정의 - let

지역변수의 정의는 let을 사용합니다. 

기본형) 

 > (let (variable declarations)
    ...body... ) 


let의 첫번째 매개변수: 변수 정의 목록 - 한 개 이상의 지역 변수를 정의할 수 있습니다.
let의 body 부분: 이 부분에서만 변수 정의 목록에 정의해둔 변수를 사용할 수 있습니다.

let의 사용예) 
 > (let (  (foo 10)
             (bar 5)  )
          (+ foo bar) )
 15 


예시에서는 foo에 10, bar에 5를 각각 정의 하였고, 이를 body 부분에서 사용해서 연산하였습니다.
let을 사용할때는, 변수 정의 목록 전체를 괄호() 로 둘러 싸야합니다. 또 변수의 이름과 변수의 값, 즉 한 쌍을 괄호로 둘러싸야합니다. 일반적으로 이름-값 정의를 할때, 정의된 변수 부분에 세로로 줄을 맞추는 것이 관례라고 합니다.


리스프에서 지역함수

지역변수를 정의할때는 let 함수를 사용했습니다. 함수는 function이라고 하죠. 리스프에서 지역함수를 정의할때는 flet 함수를 사용합니다.

정의 - flet

기본형)
 > (flet ( (function_name (arguments)
              ...function body...) )
      ... body ... ) 

flet의 첫번째 매개변수: 첫 번째 인자에서 함수를 정의합니다.
                                   여기서는 ( (function_name (arguments) ... function body...) ) 부분입니다.
flet의 body: 첫번째 매개변수로 정의된 함수를 body 부분에서 사용합니다. 
함수 정의 부분: 함수 정의 부분은 함수의 이름과 매개변수, 그리고 함수의 바디로 구성됩니다.

flet의 사용 예) 
 > (flet  ((f (n)            
                (+ n 10))) 
       (f 5))
 
15 


let 처럼 flet에서도 여러 함수를 정의하고 사용할 수 있습니다. 방식은 let과 같고, 괄호가 많아지니 신경써야 하죠.

flet, 여러 함수 정의 및 사용 예) 
 > (flet  ( (f (n)            
                 (+ n 10))
              (g (n)
                 (- n 3)))
       (g  (f 5)))
 
12


f와 g라는 함수를 정의하였고, body 부분에서 f(5)를 먼저 계산한 후, 그 값을 다시 g에 넣어서 계산한 결과를 보여줍니다. 위의 예에서는 최종 결과로 12가 나옵니다.


정의 - labels

지역함수를 하나 만들고, 그 지역함수를 다른 지역함수안에서 사용하고 싶을때가 있습니다. 
그럴때는 labels 함수를 사용해서 정의하고 사용할 수 있습니다. 기본 구조는 flet 함수와 동일 합니다.

labels 사용 예) 
 > (labels  ( (f (n)            
                 (+ n 10))
              (g (n)
                 (- (a n) 3)))
       (g  5))
 
12


labels다른 지역 함수를 실행 할 수 있게 하고, 자기 자신을 다시 실행(재귀 호출)할 수도 있습니다.