Gurugail Main/Lisp
Main

Lisp

I. LISP의 개요

LISP는 MIT의 John McCarthy 교수에 의해 1958년에 개발되었으며, 미국을 중심으로 발달된 인공지능 프로그래밍 언어의 기본 언어라 할 수 있다. 특히 LISP는 인터프리터 언어이기 때문에 컴파일이 필요 없기에, 잦은 수정이 요구되는 초기 개발 과정에서는 좋은 개발 언어라 볼 수 있다. LISP가 인공지능 프로그래밍 언어라 불리는 이유는 심볼(Symbol)들로 구성된 리스트(List)들을 쉽게 조작할 수 있는 언어이기 때문이다. LISP는 여러 가지 방언이 있지만 그 중 Common LISP가 산업, 정부기관, 대학 등에서 점차 일반화되는 추세이다. LISP의 주요 특징으로는 다음과 같다.

  • Symbolic programming (기호 처리 언어)
  • List Processing language
  • Procedural language
  • Interpreter language with compiler support

II. LISP 함수

LISP는 아톰(Atom)과 리스트라는 두 종류로 구성된다고 볼 수 있다. 아톰은 숫자나 사람, 사물 등과 같은 대상을 표현할 때 사용되는 심볼이며, 리스트는 괄호에 둘러진 아톰들로 구성된다.

+, -, *, /

기본적인 연산을 위한 함수 심벌들이 제공된다 아래와 같이 연산자가 앞에 나오며 뒤에 오퍼랜드가 연이어 표현되는 방식이다.

(+ 2 3), (- 3 4), ...

defun

LISP에서 새로운 함수를 정의하기 위해서는 “defun” 심볼을 사용한다. 간단한 예로 “half” 라는 함수를 다음과 같이 정의할 수 있다.

(defun half (n) (/ n 2))

여기서 “(“는 LISP의 시작이고, “defun”은 함수를 정의하기 위한 미리 정의된 심볼명, “half”는 함수명, “(n)”은 인수 리스트, “(/ n 2)”는 함수 몸체, “)”는 LISP 종료를 가리킨다.

CAR(first) 와 CDR(rest)

CAR(일반적으로 ‘카’로 발음함) CDR(일반적으로 ‘크더’라고 발음함) 함수는 LISP에서 중요한 함수이면서, LISP의 특성을 잘 나타낸 함수이다. LISP는 리스트 기반의 언어이기에 리스트를 핸들링 하는 CAR, CDR 와 같은 함수들이 디폴트로 구현되어 있다. (Common Lisp에서는 first와 rest라는 함수가 사용하기도 한다.) 예를 들어 (car ‘(A B C))는 리스트의 첫번째 원소인 A, (cdr ‘(A B C))는 첫번째 원소를 제외한 리스트 (B C)를 결과값으로 출력한다. 그렇다면 리스트 중 2번째 요소는 아래와 같이 정의 가능할 것이다.

(defun second (x)
(car (cdr x)))

if : 조건 처리

LISP에서의 조건문 처리에 있어 여러 가지 형태가 있지만 가장 기본적인 if 처리 방식이다.

(if test then else)

예를 들어, 100보다 크면 big, 적으면 small 이라는 결과값을 출력하는 함수를 if 문을 사용하여 정의하면 다음과 같다.

(defun big-number (x)

          (if (> x 100) ‘big ‘small))

cond : 분기 처리

cond 조건문은 다음과 같은 구문으로 구성된다.

(cond (cond-1 action-1)
      (cond-2 action-2)
      ...
      (cond-n action-n))

예)

(defun my-where-is (x)
    (cond ((equal x ‘SEOUL) ‘KOREA)
           (equal x ‘WASHINGTON) ‘USA)))

do

do는 여러 변수에 대해 초기값을 주고 일정 양만큼 증감시키면서 반복 처리할 때 사용한다.

(do ((var-1 init-1 step-1)
     ...
     (var-n init-n step-n))    ;in c/c++ for(i = 0 ; 
     (end-test action1)        ;i < 5; ..             
     (body)                    ;         { }
)

예) (do ((j 0 (+ j 1)))

        (nil)                  ;do forever 
        (format t "input ~D:" j)) 

let

LISP에서 let이라는 심볼은 초기화 기능과 함수 기능들을 함께 묶어서 사용할 수 있도록 한 기능을 가진다.

(let ((var-1 value-1)
      (var-2 value-2)
      ...
      (var-n value-n))
      body-1
      ...
      body-n)

예)

(defun rev (x)
    (let ((y (reverse x)))
         (list (first y)(second y)))))

위 예에서 'x 리스트를 역순으로 바꾸고 나서, y로 치환(assign)을 한 상태에서,
 y의 첫 번째 요소와 두 번째 요소를 사용하여 리스트를 구성하는 rev 이라는 함수를 정의'한다는 의미이다.

eval

LISP 언어는 심볼과 리스트로 구성된다는 것을 앞에서 언급하였다. 때로는 심볼을 값(Value)으로 변환하는 과정이 필요한데 바로 Evaluation 이다. 리스트에서의 심볼로만 처리하지 말고 기능으로 처리하라는 의미이다. Evaluation 예는 아래와 같다.

(setf form-to-evaluate '(+ 2 2)) (eval form-to-evaluate)

위 예에서 (+ 2 2)를 form-to-evaluate로 치환하고 eval 이라는 함수를 통해 (+ 2 2)를 실행하는 예제이다.

그리고 Evaluation에는 아래와 같은 몇 가지 규칙이 있다.

규칙 1. 숫자는 항상 그 자체로 Evaluation 된다. 규칙 2. 심볼은 그것이 가리키는 변수의 값으로 Evaluation된다.

        예) (setq x 9)를 Evaluation하면 x는 9의 값을 지닌다.

규칙 3. 쿼트(Quote)가 된 것은 쿼트가 빠진 것으로 Evaluation 된다. 규칙 4. 리스트를 Evaluation하려면, 리스트의 첫 번째 요소는 반드시 함수 또는 특수형이 되어야 한다.

예) (defun average (n m) (half (+ n m)))

t (true)

t(=truth)를 가진 절은 cond 절 위에서 모든 조건이 만족되지 않으면 수행된다.

예) (defun make-even (x)

         (cond ((oddp x) (1+x))
                (t x)))

(C++ 에서 switch 문에서의 default 기능과 유사함을 알 수 있다)

nil (false)

조건 t와 반대되는 경우 (false)를 나타내는 LISP 언어에서의 심볼이다.

defvar : 변수 선언

변수는 특정 함수에만 효력을 갖는 지역 변수와 모든 함수에 걸쳐 효력을 갖는 전역 변수로 나뉜다. 위의 예에서 half 함수 안에서 사용된 변수 n은 지역 변수이다. 전역 변수는 defvar 심볼을 사용한다.

(defvar *x*) (defun inside (*x* e) (+ e *x*))

위 예제에서 *x*는 전역 변수(LISP에서는 special variable이라는 용어를 사용)인 셈이고 e는 inside 함수 내에서만 사용되는 로컬 변수인 셈이다. *x*의 표기는 전역임을 나타내는 통상적인 습관?(약속?) 이며 반드시 *를 붙어야 하는 것은 아니다.

read

입력 스트림으로부터 읽는 함수이다.

(read)

print

출력 함수이다.

(print 'hello)

format : 문자열 함수

(format t “see ~s and ~s” ‘DICK ‘JANE)

출력 : see DICK and JANE

(C++ 에서의 % 역할을 ~ 가 대신한다고 생각하면 된다)

mapcar

리스트에 속해 있는 개별 심벌별 함수를 적용해야 하는 시나리오에 쉽게 사용될 수 있도록 한 함수이다. 가령

(defun add-3 (num) 
    (+ num 3)

3을 더해주는 함수가 있을 경우 (5 6 7)이라는 리스트 심벌들에게 개별적으로 적용하고자 할 때 아래와 같이 사용된다.

(mapcar #'add-3 '(5 6 7))

load

리스트 파일을 읽어드릴 수 있는 기능으로 별도 파일들로 리스트 파일들을 관리할 수 있도록 하여 준다.

(load "test.cl")

III. Example

Hanoi 탑 프로그램을 처리할 수 있는 LISP 프로그램의 예제이다. (이해를 돕기 위한 소스이며 완전한 소스는 아님)

;;; -*- Mode: LISP; Syntax: Common-lisp; Package: USER; Base: 10 -*- (in-package :User)
;;;=========================================================================
;;; Simple towers of Hanoi program. Note that Start-Peg and Goal-Peg are
;;; integers from 1 to 3 indicating the peg number. Ie to move 4 discs, 
;;; starting on the first peg and finishing on the last one, execute
;;; (Towers 4 1 3)
;;;

;;; 1992 Marty Hall. hall@aplcenmp.apl.jhu.edu

(defun Towers (Number-of-Discs Start-Peg Goal-Peg)
    (cond 
    ((= 1 Number-of-Discs) (format t "~%Move Top Disc from peg ~D to peg ~D."
                                         Start-Peg Goal-Peg))
    (t                     (Towers (1-Number-of-Discs) 
                                   Start-Peg (Remaining-Peg Start-Peg Goal-Peg))
                           (Towers 1 Start-Peg Goal-Peg)
                           (Towers (1- Number-of-Discs)
                                   (Remaining-Peg Start-Peg Goal-Peg)
                                         Goal-Peg))))

V. 참조