퍼온~사유..!/구조와 기능.

[펌] 인공지능 구성의 철학 (3)

온울에 2008. 4. 27. 05:07

 

-  인공지능 언어 LISP 의 철학적 기초를 중심으로 -


인공지능의 철학 : 이초식, 고려대 출판부, 1993, Page 242~267
 

1. LISP란 무엇인가?

2. LISP의 접근 절차

3. LISP의 술어처리방식

4. 조건표현의 문제

5. 역리현상의 LISP의 회귀표현

 

1. LISP란 무엇인가?

AI을 구성하기 위해서는 무엇보다도 컴퓨터 프로그램을 작동할 수 있는 언어가 필요하다. 우리는 오늘날 AI 연구에서 가장 널리 이용되고 있는 프로그래밍 언어인 LISP에서 이를 개관해 가면서 그 속에 담겨진 철학적 기초들을 밝혀 보고자 한다. 이러한 작업은 과학철학, 특히 형식과학의 철학의 시각에서 수행된다고 할 것이다. 컴퓨터의 형식게임을 가능하게 하는 프로그래밍 언어가 다룰 수 있는 범위는 논리적으로 접근할 수 있는 범위로서, 과학적 지식의 구조ㆍ성장ㆍ응용의 일반이론과 특수이론에 해당된다고 하겠다. 그러나 프로그래밍 언어의 특성에 따라 실제로 다룰 수 있는 가능한 범위는 그보다 훨씬 제한될 것이므로 이 한계를 조금이라도 넓히고 그 언어로 형성되는 작업환경을 개선해 가려는 것이 프로그래밍 언어구성의 목표이다.

LISP는 1956년 AI의 창시자인 John McCarthy 에 의해 고안된 이래로 AI에 널리 쓰여지면서 여러 가지로 보완되었고 많은 방언들이 산출되었다. 우리가 살펴보려는 것은 여러 LISP들의 공통점을 모아 구성한 Golden Common LISP 이다. 다른 프로그래밍 언어들은 수들(numbers)을 다루는 데 비해 LISP는 주로 기호처리(symbolic processing)를 할 수 있도록 고안된 것이 특색이다. 'LISP'라는 말이 LIST PROCESSING LANGUAGE 에서 유래되었듯이 그것은 리스트 처리언어라고 할 수 있다. 여기서 리스트라는 것은 지식표현의 유형으로서 독특한 의미를 지닌다. 좀더 정확히 말하면 LISP의 기본적인 데이터유형(data types)은 리스트와 아톰(atom)으로 되어 있으며 아톰은 다시금 수(number)와 기호(symbol)로 이루어져 있다.

우리는 프로그래밍 언어를 논할 때 데이터 대상(data objects)과 절차(procedures)를 구별하게 된다. 이를 요리하는 데 비유하면, 가령 '두부 찌개 백반'의 경우, 두부ㆍ고추장ㆍ쌀 등이 데이터 대상이고 그 요리법이 절차라고 하겠다. 요리의 종류에 따라 다양한 재료가 필요하고 여러 가지 종류의 그릇이 있어야 하듯이 리스트의 종류도 무척 많다. LISP에서 리스트는 좌측에서 괄호를 열고 우측에서 괄호를 닫는 형식을 취하는데, 빈 그릇처럼 아무것도 없는 리스트는 ( ) 또는 nil 로 표시한다. 리스트 속에는 아톰이나 다른 리스트를 포함할 수 있다. 이것은 마치 어떤 그릇에는 두부를 담고 다른 그릇에는 고추장을 담으며 그 모두를 담은 그릇 자체를 담는 그릇이 있는 것과 흡사하다. 그러므로 리스트는 그릇인 동시에 그 자체가 요리의 자료로 간주될 수 있으므로 리스트는 아톰과 더불어 데이터 유형에 속한다고 하였다. 아톰이라는 것이 더 이상 분할되지 않는 물질의 기본단위인 원자를 지칭하였듯이 여기에서는 나눌 수 없는 기본대상을 아톰이라고 하며 이것은 수아톰과 기호아톰의 두 가지 유형으로 크게 구분되고 있다.

    3.14
    (3.14)
    (+ 5 7)
    (THIS)
    (THIS IS A BOOK)

위에서 3.14 는 수아톰이지만 (3.14) 는 하나의 수아톰을 요소로 지닌 리스트다. 리스트는 (THIS)처럼 하나의 단어인 기호아톰을 요소로 하기도 하고 (THIS IS A BOOK)에서처럼 네 개의 요소로 된 문장을 포함할 수도 있다. 그리고 (+ 5 7)의 리스트는 첫째자리는 기호아톰이고 나머지는 수아톰들이 혼합된 것이다.

이런 데이터 유형만 보더라도 LISP의 대상세계는 수와 기호로 표현될 수 있는 모든 세계이므로, 그것이 두부, 고추장, 쌀 등의 자료를 갖고 만들 수 있는 요리보다 얼마나 다양한 요리를 만들 수 있을 것인지 능히 짐작해 볼 수 있을 것이다. 그러나 이 데이터 유형으로 만들 수 있는 요리는 두부찌개 백반처럼 먹을 수는 없다. 그것은 지능의 양식으로서 섭취, 소화해 새로운 문제 해결에 활용될 수 있는 정보이며 지식이다.

우리는 LISP의 데이터 유형에서 논리적 원자론(Logical Atomism)과 흡사한 세계관을 발견하게 된다. 세계는 원자적 사실들의 총체이고 원자적 사실은 원자 명제에 대응하므로 원자명제들의 결합으로 세계를 기술할 수 있다는 논리적 원자론은 아톰의 생각과 그 용어부터 비슷하다. 물론 LISP에서는 원자명제를 기본단위로 삼지는 않았으나, 세계는 그 어떤 기본단위들과 그들의 결합에 의해 기술될 수 있다는 생각이 유사하기 때문이다. 다만 여기서는 그 기본단위가 아톰표현과 리스트 표현으로 바뀌었을 뿐이다.

그런 다음 우리는 LISP의 모든 프로그램이 함수(functions)라는 점에 주목할 필요가 있다. Rudolf Carnap 이 관항어(functor)와 술어(predicate)를 엄밀히 구분한 것이 상기된다. 가령, 더하기와 아버지를 다음과 같이 기록했다고 하자.

    1) Plus (a, b)            ; a 더하기 b
    2) Father (a, b)         ; a 는 b 의 아버지다.

양자는 외형이 비슷하기 때문에 다 같은 명제로 생각하기 쉬우나 2)는 진위를 가릴 수 있는 명제이고 1)은 진위를 논할 수 없는 함수일 뿐이다. 명제의 경우는 말이 일단 끝났으나 함수의 경우는 아직 말이 맺어지지 않는 느낌이다. 즉 'a 더하기 b 는 c 다' 라고 해야 말이 끝나 명제로 된다. 여기서 c 는 'a 더하기 b' 의 값이라고 한다. 이를 일반화하여 수학에서 'y=f(x)'를 정의함에 있어서 y 는 x 의 함수값이라 하여 y 의 영역을 치역(range)이라고 하고 x 의 영역을 시역(domain)이라 한다. LISP의 모든 프로그램에서는 바로 이런 함수와 함수 값의 관계를 활용하였다. 예를 들어,

    *(+ 5 7)                   ; 5+7 이고 이를 식(form)이라고 한다.
    12                          ; =12 이며 이것은 윗식의 평가(evaluation)다.
    *(+ 3 9 5)                ; 3+9+5
    17                          ; =17

LISP는 위의 보기에서처럼 함수를 리스트로 제시하면, 즉 괄호를 닫으면 그 함수값이 나온다. 이리하여 마치 묻고 대답하는 형식처럼 보인다. 위의 예  *(+ 5 7) 에서 맨 앞의 별표는 무엇인가를 타이프 해주기를 기다리는 초기상태 라고 하겠으며 괄호 열고 맨 첫자리 +는 관항어로서 이를 절차명(procedure name)이라고 한다. 다음에 칸을 떼어 기술한 5와 7은 각기 논항(argument)을 이룬다. 그리고 마지막으로 괄호를 닫고 식을 마무리하면 그 식에 대한 평가 12가 나타난다. 산술에서 + 는 두 개의 논항 사이에 놓았으나 LISP에서는 리스트의 첫 자리를 절차명으로 한 다음 위에서처럼 둘 이상의 논항을 제시한다. 여기서 절차명인 '+' 자체가 기호아톰임을 주목해 볼 필요가 있다. 앞의 요리 비유에서 데이터 대상인 두부나 쌀 등과 절차를 지시하는 요리법을 구분했는데 절차명 자체도 데이터 유형의 하나가 되었기 때문이다. 그러므로 절차도 일종의 데이터 유형으로 간주된다.

 

2. LISP의 접근 절차

컴퓨터가 형식게임을 하는 시스템이라는 것을 상기해 보면서 LISP의 구조를 살피기로 한다. LISP는 기본 토큰들을 갖추고 있으며 이들을 다루는 여러 가지 규칙들이 마련되어 있다. LISP에는 앞서 살펴본 산술의 더하기 기호 +, 빼기 기호 -, 곱하기 기호 *, 나누기 기호 /ㆍ=ㆍ<ㆍ> 등과 더불어 아라비아 숫자들이 있어 수학적 표현들을 다룰 수 있고 알파벳과 각종 기호들이 있기 때문에 영어 등의 일상언어도 표현할 수 있다. 그러나 LISP의 문법은 일상언어의 경우와 달리 인공적으로 엄밀히 규정되어 있다. 장기에서 궁, 차, 마, 포, 상, 졸 등이 각기 행마 규칙이 다르듯이, 서로 달리 쓰이는 규칙의 토큰들이 많이 내장되어 있다. 그중에는 기호들의 값을 바꾸는 기호, 새로운 절차를 규정하는 기호, 새로운 요소를 리스트 안에 추가하는 기호 등 사용자들이 자유롭게 만들어 이용할 수 있는 장치가 되어 있기 때문에, LISP로 접근할 수 있는 세계는 무척 넓고, 다른 컴퓨터 언어들보다 창의성을 발휘할 기회가 많은 장점을 지니고 있다. 그리하여 장기를 두려면 우선 행마규칙부터 익혀야 하듯이, LISP라는 형식게임을 하려면 기본 토큰들의 규칙부터 알아야 한다.

우선, 디지털 컴퓨터가 읽기와 쓰기의 돌림을 한다는 점과 연계하여, LISP가 갖추고 있는 기본적인 절차 토큰들로부터 살펴보자. 그러면 자료들을 끄집어내는 절차와 자료들을 집어넣는 절차, 그리고 자료를 지닌 새로운 것을 만들어내는 절차, 이렇게 세 가지 종류의 절차가 필요한데 이런 절차를 수행하는 자를 각기 읽는이(Reader), 쓰는이(Writer), 엮는이(Constructors)라고 하며 이 세 가지 절차를 총칭하여 '접근절차'(Access Procedures)라 한다. 이 세 가지 유형의 절차 중에서 가장 기본적인 것 몇 가지만 검토해 봄으로써 LISP의 구조를 알아 보도록 하자.

LISP의 가장 중요한 자료는 리스트이므로 리스트를 읽고 쓰고 엮는 절차가 필요하다. 기초적인 읽는 이들은 FIRST, REST, LAST, LENGTH이다. FIRST는 리스트 안의 첫째 요소를 읽어내라는 기호이고 REST는 첫째 요소를 빼낸 다음 나머지 리스트를 읽어내라는 기호이며 LAST는 마지막 요소만을 제외한 모든 요소를 빼낸 리스트를 읽어내라는 기호이다. 그리고 LENGTH는 하나의 리스트 안의 요소들의 수를 읽어내라는 기호이다. 여기 기호들은 영어로 되어 있으므로 이해하기 쉬우나, 초기에 LISP를 IBM 704에 적용하도록 만들었을 당시에는 FIRST를 CAR로, 그리고 REST를 CDR로 사용했다. 오늘날도 이 표기법을 그대로 사용하는 프로그래머들이 많다.

그런데 이런 종류의 논의에서 우리는 기호와 기호의 값을 구별할 필요가 있다. 가령, 고려대학생이라는 기호와 고려대학생을 구별하기 위해, 전자를 '고려대학생'이라고 일상언어에서도 달리 표현하는 방식을 우리는 LISP에서도 발견할 수 있다. 예를 들어, 봄ㆍ여름ㆍ가을ㆍ겨울의 사계절을 S 라고 하였을 경우, S 는 봄 여름 가을 겨울로 된 리스트를 값으로 하지만, 'S 처럼 문자나 리스트 왼편 위쪽에 따옴표를 붙이면 그 값은 그 문자 자체가 된다. 그리고 'S 를 봄 여름 가을 겨울의 리스트로 앞으로 사용하겠다'고 하고 싶으면 Setf를 사용할 수 있다.

    *(SETF S' (SPRING SUMMER FALL WINTER))
     (SPRING SUMMER FALL WINTER)
    *S
    (SPRING SUMMER FALL WINTER)

여기서 원초기호 SETF는 SET Field를 줄인 말이다. 이 기호를 활용하면 이처럼 임의의 새 기호 S 를 사용하여 대신하고 싶은 기호들을 그 값으로 삼을 수 있다. 그러나 여기서 주의할 것은 S 의 값은 리스트 기호이기 때문에 리스트 앞괄호 윗부분에 따옴표를 붙여야 하며 이를 붙이지 않으면 잘못이라는 표시가 나온다.

    *(SETF S (SPRING SUMMER FALL WINTER))
    ERROR:
    Undefined function: SPRING
    while evaluating: (SPRING SUMMER FALL WINTER)

LISP에서는 통상적인 경우, 지정된 절차를 사용하기 이전에 좌측으로부터 우측으로 모든 형식들의 모든 논항을 평가한다. (+ (* 5 2) (/10 2)) 의 경우, 두 개의 논항 (* 5 2) 와 (/10 2) 모두 좌측으로부터 우측으로 평가하고 그 곳에 + 의 더하기 작업을 한다. SETF 는 이런 규칙의 중요한 예외이다. 그 첫째 논항은 평가되는 것이 아니고 그 기호의 값을 제공하는 둘째 논항이 평가되기 때문이다. 그런데 리스트 안의 리스트로 제시된(SPRING SUMMER FALL WINTER) 안에서 첫째 항은 그 리스트의 다른 항들을 평가하는 함수여야 한다고 보는데 그것이 정의되지 않아 잘못되었다는 것이다. 위의 읽는이(reader) 들의 예는 다음과 같다.

    *(FIRST S)
    SPRING
    *(FIRST' S)
    ERROR:
    CAR OR CDR OF NON-LIST OBJECT: S
    *(S)
    ERROR:
    Undefined function: S
    while evaluating: (S)
    *(REST S)
    (SUMMER FALL WINTER)
    *(LAST S)
    (WINTER)
    *(LENGTH S)
    4
    *(FIRST (REST S))
    SUMMER

LISP의 진행이 문답식 대화로 보이는 것은 *표 난에 제시된 함수형을 묻는 것이라고 하면 밑의 난에 제시되는 대답으로 간주할 수 있기 때문이다. 그리고 잘못 물었을 경우에도 친절하게 무엇이 어떻게 잘못되었다고 지적해 주므로 편하게 물을 수 있다. 물론 위의 Setf 의 경우처럼 예외적으로 쓰기도 하고 앞으로 살펴보겠지만 LISP에도 묻는 형식이 따로 있기는 하지만 전체적으로 함수와 그 값의 기법이 대화체를 이룬다는 점이 철학적 대화의 가능성도 기대해 볼 수 있도록 해준다.

특히 기호의 값을 묻는 기호와 그 기호 자체를 묻는 기호의 구별은 매우 중요하다. 위의 설명에서는 일상언어에서도 따옴표로 구별해오던 바라고 간단히 언급했으나 실상 학문연구나 일상생활에서 이 구분이 잘 되지 않아 많은 혼란이 있어 왔다. 그리하여 현대 분석철학들은 대상언어(object language)와 그 대상언어에 관한 언어로서의 메타언어(meta-language)를 구분하였다. 이 구분을 못하였을 경우에 많은 혼란이 야기된다. 우리는 그 구분에 의거하여 해결한 문제들 중의 하나로서 이른바 에피메니데스의 역리(Epimenides' Paradox)를 꼽을 수 있다.

위에서는 LISP가 마련하고 있는 원초적인 읽는 이들로서 FIRST, REST 등을 몇 가지 살펴보았고, 이제는 원초적인 엮는 이(constructor) 로서 CONS 의 기능을 우선 검토해 보도록 한다. CONS는 CONStruct의 줄인 말로서 이미 있는 하나의 리스트 안에 요소를 다음과 같이 추가할 때 사용한다.

    *(CONS' A'(B C))
    (A B C)

그리고 우리는 이것을 기호의 값을 바꾸는 원초기호 SETF 와 결합해 사용할 수도 있다.

    *(SETF L'(Y Z))
    (Y Z)
    *L
    (Y Z)
    *(SETF L (CONS; X L))
    (X Y Z)
    *L
    (X Y Z)

그런데 LISP는 원초적인 절차들을 갖추고 있을 뿐 아니라 새로운 절차들을 만드는 절차까지도 갖고 있기 때문에 잘못을 쉽게 고칠 수도 있고 기존의 절차들을 이용해 새로운 절차를 창의적으로 만들어낼 수도 있다. 그 중에서 단순한 절차 창조의 원초기호는 DEFUN이다.

가령, 우리가 어떤 리스트의 양쪽 가장자리에 있는 요소를 뽑아 새로운 리스트를 만든다고 하자. 그런 절차가 LISP에 원초적 절차로서 마련되어 있지 않으므로 이미 원초절차로 갖추고 있는 것을 활용해 그런 절차를 만들어야 할 터인데 이런 절차 창조의 절차가 DEFUN으로 이루어진다.

    *(SETF M' (A B C D E F))
    (A B C D E F))

여기서 첫째요소 A 와 마지막 요소 F 를 뽑아 합한 (A F)라는 리스트를 만들려고 한다. 그러면 (CONS (FIRST M) (LAST M))이라고 이미 알려진 FIRST, LAST, CONS의 원초기호를 사용하면 된다. 그러나 이것이 번거로워 간편하게 '양쪽 가장자리 요소를 뽑아 리스트를 만들라'는 절차명을 YANGKA 한 마디로 그 일을 수행하려면 다음과 같이 하면 된다.

    *(DEFUN YANGKA (M) (CONS (FIRST M) (LAST M)))
    YANGKA
    *(YANGKA' (RED YELLOW GREEN BLUE))
    (RED BLUE)

여기서처럼 DEFUN의 사용에 따르는 요소들을 모아 놓는 구조 패턴인 조형(Template)은 다음과 같다.

    (DEFUN <절차명>        ; YANGKA와 같은 임의의 명칭
    <매개변항명>)             ; M으로 표시된 변항의 명칭
    <형식> )                    ; CONS로 시작되는 틀에 해당

이 조형을 일반화하면 매개변항명도 <변항명 1>, <변항명 2>, ... 등으로 여러 개 설정할 수 있고 형식도 <형식 1>, <형식 2>, ... 등으로 많이 다룰 수 있다. 이렇게 다양하게 절차처리를 할 수 있기 때문에 LISP는 원초적 절차를 이용해야 한다는 제약을 받지만 그들을 창의적으로 결합함으로써 새로운 절차들을 만들 수 있는 자유의 가능성이 부여되었다고 하겠다.

뿐만 아니라 논리학자 처치(A. Church)가 개발한 명칭 없는 함수의 표기법인 Lambda 표현을 이용하여 절차명까지도 없애고 DEFUN의 절차 창조의 기능을 하는 기법을 창안해 LISP을 더욱 편리한 언어로 구성하였다. 위의 양가 선발 예에서 본다면 DEFUN과 YANGKA의 절차명을 제외하고 (LAMBDA (M) (CONS (FIRST M) (LAST M)))를 사용할 수도 있을 것이다.

LISP는 CONS 이외에도 constructor 로서 LIST, APPEND, REVERSE 등을 갖고 있다. LIST는 요소들의 집합으로부터 하나의 리스트를 만들 경우에 사용되고 APPEND는 두 개의 리스트의 요소들을 하나의 리스트로 결합하는 데 쓰여지며 REVERSE는 요소들을 거꾸로 배열해 리스트를 만들 때 사용된다. 다음의 보기가 이들 constructor 의 차이를 잘 나타내 준다.

    *(SETF M ' (A B) N ' (C D))
    (C D)   ; SETF가 다수의 값을 가질 때는 마지막 값만 제시한다.
    *(CONS M N)
    ((A B) C D)
    *(LIST M N)
    ((A B) (C D))
    *(APPEND M N)
    (A B C D)
    *(REVERSE M)
    (B A)

LISP의 접근절차 중에서 reader 와 constructor 의 원초기호 몇 가지를 소개했으며 그 writer 의 원초적 기호들은 작업한 것을 보관하는 일이다. 앞자리에 절차기호를 사용하는 LISP의 표기법은 포란드 논리학파의 표기법을 응용했다고 하겠다.

 

3. LISP의 술어처리방식

우리는 LISP가 함수형식을 취하고 있으며 관항어는 술어와 구분된다는 점을 앞에서 지적한 바 있다. 그러면 여기서는 술어와 술어논리는 어떻게 다루어지고 있는가? LISP에서는 술어도 함수처럼 다루기 위해 술어를 참이나 거짓을 산출하는 절차로 취급한다. 이것은 술어가 논항과 결합하여 명제가 되고 명제는 참 아니면 거짓의 진리값을 갖는 점에 착안한 것이다. 그리하여 LISP에서는 우선 참을 T, 거짓을 NIL이라고 하고 이 진리값을 바꾸는 원초기호를 NOT으로 규정하여 논리학의 진리표방식을 택하였다. 여기서 중요한 것은 T 의 기호도 T 이고 T 의 값도 T 이며 NIL도 마찬가지로 그 기호와 값이 모두 NIL이라는 점이다.

술어를 T 나 NIL을 검사하는 절차로 보게 되면 그 검사의 기준이 되는 '같다'는 술어의 의미를 밝힐 필요가 있다. 전통논리학에서 논리적 사고의 기본원리로서 동일률을 꼽아온 것이나, 어린이들의 사고 전개과정에서 무엇이 무엇과 같다거나 같지 않다는 것을 가리는 일부터 해온 것은 동일률의 중요성을 잘 설명해 준다. 무엇이 무엇과 같다고 할 때 어떤 측면에서 같은 것인지를 밝혀야 한다. 이를 밝히지 않고 서로 다른 기준에 의거하여 같다거나 같지 않다고 하였을 경우, 사고의 혼란을 일으키고 때로는 쓸데없는 논쟁을 하게도 된다. 의미론의 문제로서 '샛별과 저녁별이 같다'는 예가 논의의 대상으로 자주 거론되어온 것도 그들이 같다는 기준을 밝혀야 했기 때문이다. 그들이 지시하는 바가 같다는 외연적 동일성과 그 개념의 내표가 같다는 것은 엄연히 구분되어야 할 것이다.

산술의 '2 + 3 = 5'에서 '+'는 관항어(functor) 인 데 비해 '='는 술어로 구분되며 후자는 '같다'는 술어에 해당된다. 그것은 일상언어로 '둘 더하기 셋은 다섯과 같다'로 표현된다. 그러나 '둘 더하기 셋'은 '다섯'과 같지 않다고 할 수 있다. 그러므로 같은 것을 나타내는 기호를 구별할 필요가 있다. LISP는 데이터 유형으로서 아톰과 리스트, 그리고 아톰은 수와 기호를 구분하였으므로, '같다'는 술어도 이런 표현과 관련하여 '같다'는 술어를 달리 사용한다. 그리고 이 술어를 함수형으로 하기 위해 묻는 방식을 취하였다. EQL은 두 논항의 값이 같은 기호(같은 유형) 인지, 그리고 = 은 두 논항 값이 같은 수인지를 묻는 원초기호로 간주되었다.

    *(EQUAL (+ 2 3) 5)
    T
    *(=(+2 3 ) 5)
    T
    *(EQL 4 4.0)            ; 수들이 다른 유형이기 때문이다.
    NIL
    *(= 4 4.0)                ; 다른 유형의 수이나 = 를 만족시킨다.
    T

다음으로 LISP가 지닌 데이터 유형을 검사하기 위한 원초기호들로서, 아톰을 묻는 ATOM, 수를 묻는 NUMBERP, 기호를 묻는 SYMBOLP, 그리고 리스트를 묻는 LISTP 등이 있고 수 중에서도 영인지를 묻는 ZEROP, 양의 수인지를 묻는 PLUSP, 음의 수인지를 묻는 MINUSP, 홀수를 묻는 ODDP 등이 있다. 그리고 리스트들을 검사하는 것으로서 가장 중요한 원초적 기호는 NULL과 CONSP와 MEMBER이다.

어떤 것이 공 리스트(empty list) 인지를 알아 보려면 NULL을 사용한다. 따라서 NULL은 논항이 공 리스트인가 하는 물음으로 풀이된다. 어떤 리스트가 공 리스트가 아니라면 그것은 명시적이든 암묵적이든 어느 한 CONS의 결과로 간주된다. 그리하여 논항이 CONS일 수 있는 것인가를 묻는 CONSP는 리스트 검사에서 필수적이다. 그리고 더욱 주목할 만한 것은 MEMBER이다. 이것은 첫째 논항이 기호이고 그것이 둘째 논항이 되는 리스트의 원소인지를 알아보기 위해 사용하는 술어이다. 그리고 첫째의 기호는 둘째 리스트의 최상위 수준의 원소(a top-level element)이어야 한다.

    *(MEMBER' MOTHER' ((FATHER SON) (MOTHER DAUGHTER)))
    NIL            ; 여기서 최상위 수준 원소는 (FATHER SON)와 (MOTHER DAUGHTER)이며 MOTHER는 그 원소의 원소이다.
       가령, KWA-IL의 값은 (FRUIT APPLE)이라고 하고 3SSANG의 값은((SPORT RIDING) (FRUIT APPLE) (VEGETABLE CARROT))라고 가정하자. 이런 가정에서 다음을 보자.
    *(MEMBER KWA-IL 3SSANG)
    NIL             ; (EQUAL KWA-IL' (FRUIT APPLE))의 값은 T 이지만 MEMBER의 검사는 EQL을 사용하기 때문이다.
    *(MEMBER KWA-IL 3SSANG        : TEST' EQUAL)
    ((FRUIT APPLE) (VEGETABLE CARROT))    ; 위의 :TEST 말이 EQL로 하지말고 EQUAL로 검사를 지적했으므로.

술어들을 결합하는 절차는 명제논리의 결합사 (connective) 들의 진리치관계에 대체로 따르게 된다. 그러나 이미 살펴보았듯이 LISP의 표기법은 통상적인 수학이나 논리학의 것과는 달리 포란드학파의 것을 채용하고 있다. 즉, 산술의 덧셈은 "2 + 4 + 7 + 5 + 9" 대신 "(+ 2 4 7 5 9)"를 사용함으로써 관항어 +를 반복하지 않아도 되었다. LISP의 술어 결합에 있어서 포란드식 표기법은 더욱 뚜렷하다. 가령, 통상적인 연언명제 p ∧ q 는 포란드식으로는 ∧ p q 이며 LISP 에서는 (And p q)로 표기되기 때문이다. 두 개 이상의 술어들의 결합을 검사함에 있어서, 술어들의 연언(and) 의 경우는 하나의 술어가 거짓이라도 거짓이 되며 술어들의 선언(or) 의 경우는 하나의 술어가 참일지라도 참이 된다는 점이 활용된다. 그리하여 연언 And는 왼편에서 오른편으로 논항들을 평가해 가면서 NIL이 나오면 NIL로 결정하고 모두 검사해 NIL이 없으면 T 로 판정한다. 그리고 선언 OR 도 왼편에서 오른편으로 논항들을 평가해 가는데 이때에는 어떤 것이 NIL이 아닌지를 평가해 간다. 여기서 non-NIL은 반드시 T 가 아님을 유의해 볼 필요가 있다.

    *(OR (LIST PI) (NUMBERP PI))
    (3.14)            ; (LIST PI)는 NIL이 아니므로
                        ; 둘째 논항은 무시되었다.
    *(OR (LISTP PI) (NUMBERP PI))
    T                  ; PI는 리스트는 아니지만 수이다.
    *(OR (LISTP PI) (= PI 0))
    NIL                ; PI는 리스트도 아니고 영도 아니다.
    *(AND (LISTP PI)(NUMBERP PI)
    NIL                ; PI는 리스트가 아니기 때문이다.
    *(AND (LIST PI) (NUMBERP PI)
    T                   ; T 가 된 것은 (LIST PI)는 (3.14)가 되어 NIL이 아니며 PI가 수이기 때문이다.
    *(AND (NUMBERP PI) (LIST PI))
    (3.14)             ; PI는 수이므로 넘어가 LIST를 작동한다.

우리는 여기서 LISP의 연언(and) 과 선언(or) 이 논리적인 그것들과 여러 가지로 유사하지만 차이가 있음을 발견하게 된다. 그 표기법이 다른 점은 이미 언급되었으며 연언 표현이나 선언 표현의 값이 T 나 NIL 이외의 것으로도 기록될 수 있다는 점이다. 그 까닭은 T 가 아닌 non-NIL의 값도 평가의 대상이 되며 그 평가는 순차적으로 왼편에서 오른편항에로 진행되기 때문이다. 따라서 평가의 값이 T 와 NIL의 둘뿐이 아니고 그 이상이 될 수 있다는 점에서 이치논리(two-valued logic)을 넘어선 다치논리(many-valued logic)를 수용하는 방식이라고 하겠다. 논항의 순서에 관계없이 사용될 수 있는 논리적 연언이나 선언과는 달리, LISP-AND와 LISP-OR에서는 교환법칙이 성립되지 않는다는 것은 위의 보기에서도 잘 나타나 있다.
우리가 LISP의 AND와 이치논리에 기반을 둔 논리적 연언을 비교하기 위해 다음과 같이 정의하여 사용해 보자.

    *(DEFUN LOGICAL-AND (X Y) (AND X Y T))
    LOGICAL-AND
    *(LOGICAL-AND (NUMBERP 'FRED) (ODDP ' FRED))
    ERROR: ODDP : Wrong type argument: FRED
    An INTEGER was expected
    *(AND (NUMBERP' FRED) (ODDP ' FRED))
    T

위에서 보는 바와 같이 논리적 연언은 논항의 평가 여부를 제어할 수 없어 LISP의 연언보다 매우 제한되어 있다. 그러나 이치논리의 기반인 부울 대수(Boolean Algebra)의 1 과 0 들은  Claude Shannon 에 의해 전기 스위치 회로로 표현될 수 있음을 밝히게 되었고 이것이 오늘날 컴퓨터의 회로에 적용된 만큼 LISP의 연언이나 선언도 결국 0과 1의 값으로 전개되는 원초적인 논리적 연산자(primitive logical operator)에 의존한다고 하겠다.

 

4. 조건표현의 문제

조건언의 표현은 무척 다양한 사고유형으로 쓰여질 뿐만 아니라 논리적 사고나 인공언어 구조의 기초적인 요인이 되기 때문에 중요하면서도 때로는 혼란을 많이 야기해온 문제이기도 하다. 어떤 논리적 사고도 추리의 형식을 취하고 추리란 '만약 전제이면 결론이다'는 조건언의 형식으로 표현되므로 조건표현이 연역적인 도출가능성과도 결부되어 왔으며, 함수형식을 취하는 LISP의 경우도 '만약 함수형으로 물으면 함수값을 대답한다'로 되어 LISP 자체도 조건언의 표현으로 풀이될 수 있다. 그런데 조건언의 논리의 대표적인 형식인 실질함축(material implication)은 이른바 역리현상(paradox) 에 부딪침에 따라, 이를 해소하기 위해 실질함축의 이치논리적인 진리함수관계에 대한 여러 가지 음미와 비판이 있었고 그를 통해 새로운 논리들이 개발되기도 하였다.

LISP의 조건표현들도 그러한 새로운 논리개발의 한 가지 형태로 볼 수 있다. p → q의 형식으로 표현되는 명제논리의 실질함축 이란 주지하는 바와 같이 ∼p ∨ q나 ∼(p ∧ ∼q)로 정의되며--이 정의에는 참과 거짓이라는 두 개의 진리치 관계만이 고려된다-- 이런 이치논리를 기반으로 하여 그것들의 일치 여부만을 논하는 진리함수의 논리였다. 그리고 이 실질함축을 일상언어로는 조건언 형식으로 '만약 p면 q' (if..., then...)라고 호칭해 왔다.

그런데 실질함축에 의하면 전건인 p가 거짓이면 후건인 q의 진리치에 관계없이 그 실질함축은 참이 된다. 다시 말하면, '김군이 대학에 입학하면 나는 김군에게 구두를 사 주겠다'고 한 말은 김군이 대학에 입학하지 못하면 참이 된다는 것이다. 이것은 일상언어의 조건언 의미와 배치된다. 그리하여 그것은 이른바 실질함축의 역리로 호칭되어 왔다. 그러면 이치논리의 컴퓨터 회로를 기반으로 하여 프로그램을 작성해야 하는 LISP는 이러한 문제를 어떻게 해결할 수 있도록 조건표현을 하고 있는 것일까?

LISP가 조건표현을 위해 마련한 원초적 기호로는 WHEN, UNLESS, IF, COND가 있다. 이들의 절차를 모범적으로 제시하는 조형(template)과 그 실례들을 살펴 보면서 실질함축의 역리해소를 어떻게 시도했는지를 알아보도록 하자. 가령, 온도와 같은 변항의 값을 검사하여 그 결과가 추운지, 더운지, 적당한지를 알려는 경우처럼, LISP의 원초적 조건기호들은, 우리가 알고자 하는 값이 어떤 검사의 결과에 의존하는 경우에 사용할 수 있도록 고안되었다. 일반화된 조건기호는 CONDitional에서 따온 COND 이지만 그보다 단순한 것은 IF 이고 WHEN 과 UNLESS 는 IF의 특수한 경우라고 할 수 있으므로 IF 부터 알아 보기로 한다.

IF의 모조형은 다음과 같다.
(IF <검사형> <제2형> <제3형>)
IF형식에 있어서 첫째 논항은 무엇을 검사하기 위해 작동하는 형식(trigger or test form)인데 그 검사결과가 T 이거나 NIL이 아닌 것으로 되면 제2형을 평가하고 그 검사 결과가 NIL이면 제3형을 평가한다. 다음의 보기는 변항 X 를 온도가 화씨 50도인 물이라고 했을 경우이다.

    *(IF ( > X 32)'LIQUID-OR-GAS' SOLID)
    LIQUID-OR-GAS

그런데 조건표현 WHEN은 검사형이 T 나 non-NIL 인 경우 제2형을 평가하고 검사결과가 NIL 이면 NIL 로 평가하는 형식이다. 이것은 (WHEN <검사형> <제2형>)의 모조형으로 표현된다. 조건표현 UNLESS 는 그 검사형이 NIL 인 경우, 제2형을 평가하고 non-NIL 이면 NIL 로 평하는 것으로서 (UNLESS <검사형> <제2형>)의 모조형을 취한다.

    *(WHEN(> X 32) 'LIQUID-OR-GAS)
    LIQUID-OR-GAS
    *(WHEN ( < X 32) 'SOLID)
    NIL
    *(UNLESS ( > X 32) 'SOLID)
    NIL
    *(UNLESS ( < X 32) 'LIQUID-OR-GAS)
    LIQUID-OR-GAS


    일반적인 원초적 조건기호 COND의 모조형은 다음과 같다.

    (COND       ( <검사형 1> <결과 값 1> )
                    ( <검사형 2> <결과 값 2> )
                    . . .
                    ( <검사형 n> <결과 값 n> ))

검사한 결과가 non-NIL일 때만 그에 대응하는 결과 값을 평가하게 된다. 그리하여 검사형 중에는 가능한 모든 경우를 모두 열거하여 평가하면 해당하는 적절한 값을 얻을 수 있다. 조형의 마지막 <검사형 n>는 T 로 대치하여 흔히 사용하는데 그것은 나머지 경우 모두를 지칭하므로 편리하게 프로그램을 작성할 수 있다.

    *(SETF P .6)
    *(COND    ((> P .75) 'VERY-LIKELY)
                   ((> P .5) 'LIKELY)
                   ((> P .25) 'UNLIKELY)
                   ( T 'VERY-UNLIKELY))
    LIKELY

위에서 살펴본 LISP의 조건표현들을 명제논리의 실질함축과 비교하기 위해 진리표(truth-table)를 만들어 보자. 진리값 NIL은 F 로 정확히 번역되지만 non-NIL은 T 와 완전히 일치하지 않는다. 그러므로 아래 도표에서 표시된 T 와 완전히 일치하지 않는다. 그러므로 아래 도표에서 표시된 T 는 논의의 편의상 단지 non-NIL을 대표하는 것으로 간주한다.

 

p

q

p→q

(WHEN p q)

(UNLESS p q)

(IF p q)

(COND p q)

1
2
3
4

T
T
F
F

T
F
T
F

T
F
T
T

T
F
F
F

F
F
T
F

T
F
?
?

T
F
?
?

WHEN의 경우, 검사형인 p 가 non-NIL일 때 제2형인 q 의 값에 따라 결정되고 전자가 거짓일 경우는 모두 거짓으로 되었고, UNLESS는 검사형이 거짓일 때에만 제2형을 평가하고 나머지 경우는 모두 거짓으로 처리하였기 때문에 위와 같은 진리표가 성립된다. 그러나 IF 와 COND의 경우는 검사형이 거짓이면 두 개의 논항만으로 결정할 수 없고 세번째 논항의 값에 의거하여 평가되므로 두 개의 논항만으로는 결정될 수 없고 오직 검사형이 거짓이 아닐 경우만 평가될 뿐이다. 이리하여 검사형이 거짓이면 언제나 참으로 판정되지 않기 때문에 실질함축의 역리는 발생하지 않는다. 그러나 LISP의 조건표현들에서는 non-NIL이 T 이외에 다른 기호값으로도 제시되기 때문에 이치논리의 실질함축에서처럼 진리치의 연산을 할 수 없다고 하겠다.

 

5. 역리현상의 LISP의 회귀표현

자연언어와 인공언어에서 혼동이나 오해를 야기시키는 문제들 중에는 자기지시적 언어의 역리들이 있다. 19세기 말과 20세기 초에 걸쳐 활발히 논의되었던 수학의 기초이론 분야에서 러셀의 역리(B. Russell's Paradox)로 대표되는 집합론 또는 논리적 역리와 거짓말쟁이의 역리로 지칭되는 인식론 또는 의미론적 역리 등이 그러한 부류에 속한다. 그리고 초기 비트겐슈타인과 논리실증주의의 차이점에서와 같이, 1930년대 초에 카르납이 인공언어 구성에 관한 비트겐슈타인의 견해를 비판한 것도 바로 자기지시적 언어를 중심으로 전개한 것이었다. 이처럼 20세기 초에 활발히 전개되었던 논리 및 수리철학적 논의의 결과들은 특히 LISP의 회귀함수 처리에 잘 반영되었다고 하겠다.

러셀의 역리란 모든 수학의 기초가 되는 집합의 개념을 명확히 밝히려는 철학적 탐구과정에서 발견되었다. 통상적인 집합들은 하나 이상의 원소들의 집합이지만 하나의 원소만 가진 것도 집합이라고 하며 심지어는 하나의 원소도 갖지 않은 것도 공집합(null set)이라고 하여 집합의 명칭을 붙인다. 그리고 통상적인 집합론에 의하면 집합들은 자기 자신도 자기의 원소로 삼는 것이 정상적이므로, 이렇게 '자기 자신을 원소로 삼는 집합들의 모든 집합'을 N 이라고 하자. 그런데 러셀이 상정한 집합은 그런 정상적 집합과는 달리 '자기 자신을 원소로 삼지 않는 집합'이다. 이리하여 자기 자신을 원소로 삼지 않는 모든 집합들의 집합을, N 과 구별하는 러셀의 집합이라는 뜻에서 R 로 호칭하면,

    R = { X : X ∉ X }

이 된다. 그런데 이 R 은 자기 자신에 속하는가 속하지 않는가 하는 것이 문제다. 만약 R 이 R 에 속하지 않는다면 그것은 R 의 정의에 의해 R 에 속한다. 뿐만 아니라 만약 R 이 R 에 속한다면 그것은 다시금 R 의 정의에 의해 R 에 속하지 않는다는 역리현상에 부딪친다.

다른 한편, 의미론적 역리로 호칭되는 거짓말쟁이의 역리는 고대 그리스 철학자들의 논의 중에서 제기되었던 것이다. 크레테 사람 에피메니데스는 "모든 크레테 사람은 거짓말쟁이다"라고 말하였다. 이 말은 참말인가 거짓말인가? 만약 그가 참말을 하였다면 그것은 거짓말이고 만약 그가 거짓말을 하였다면 그것은 참말이다. 이 역리현상을 좀더 단순화하여 말한다면 '내가 지금 말하고 있는 것은 거짓말이다'라는 표현 S 로 나타낼 수 있다.내가 거짓말을 했다면 그것은 S 에 의해 참말이고 만약 내가 참말을 했다면 그것은 S 에 의해 거짓말을 한 것이 되니 말이다.

이와 같은 역리현상들은 얼핏 보면 말장난처럼 보이기도 하나 1903년 러셀이 발표한 집합론의 역설은 당시 수학계에서 정설로 굳어가던 칸토어(G. Cantor)의 집합론의 기초를 뒤흔들게 되었고 수학계의 위기로 간주되는 큰 사건이었으며 그 해결의 방향이 현대 수학의 행방을 규정해 줄 정도였다. 이와 관련된 의미론적 역설은 언어의 수준을 구분함으로써 그 역리현상 발견은 현대에서 진리론 연구와 언어분석의 새로운 전환점이 되었다.

러셀 자신은 집합론의 역리를 유형론(theory of types)을 제시하여 해결하였다. 우리가 술어논리에서 'x 는 p 다'라고 했을 때 이것이 의미를 지니기 위해서는 x 와 p 의 범위가 각기 규정되어야 하는 것처럼, 'X 가 Y 에 속한다'고 했을 때도 각기 그 범위들이 설정되어야 한다. 집합에서 가장 낮은 유형은 개체들이며, 개체들의 집합, 그 집합의 집합으로 유형이 차례로 높아간다. 그러므로 가령, 개체들 a, b, c의 집합 A 의 유형은 그 개체들 a 보다 한 단계 높은 유형이다. 이와 같이 'X ∈ Y'에 있어서 Y 의 유형(type)은 X 의 유형보다 한 단계 높아야 함에도 불구하고 이 유형의 차이를 무시하고, 그들의 유형을 동일한 것으로 취급하는데서 역리현상이 나타난다고 보았다. 그러므로 'X ∈ X'에서도 앞의 유형을 뒤의 유형보다 한 단계 낮은 것으로 취급하면 역리가 발생하지 않는다는 것이다. 특히 러셀은 이런 종류들의 역리를 피하기 위해 준수해야 하는 악순환의 원리(vicious circle principle)를 제시하였다. 그것은 어떤 집합도 그 전체에 의해서만 정의될 수 있는 그런 원소를 가질 수 없다는 내용의 원리다.

집합론의 역리가 자기 지시(self reference)의 유형을 혼동한 것이라면 의미론의 역리는 언어의 수준을 혼동한 것으로 지적되었다. 타르스키(A. Tarski)는 진리 개념을 그것을 표현하는 언어에 의존하는 것으로 판정하였다. 어떤 주장이 참이라고 할 때 그것이 참이 된다는 의미를 분명히 하기 위해서는 그것을 표현하는 언어의 수준이 제시되어야 한다. 특히 대상언어(object language)와 메타언어(meta language)의 수준이 구분되지 않는 자기지시적 언어를 사용하게 될 때 거짓말쟁이의 역리와 같은 의미론적 역리가 발생한다는 것이다. 예를 들어, '모든 크레테 사람이 언어 L1 로 말하는 것은 거짓말이라고 에피메니데스는 언어 L2 로 말하였다'고 말이다. 이렇게 언어수준을 구분하면 역리는 해소된다는 것이다. 대상언어와 메타언어의 구분은 현대 분석철학에서 개발한 명석화작업의 중요한 업적이며 그 도움을 배경으로 하여 프로그램언어인 LISP에서도 기호와 기호 자신을 나타내는 기호를 구분하기 위해 따옴표가 쓰여지게 되었다고 하겠다.

그러면 자기 지시(self reference) 에서 언어수준을 구분해야 한다는 것은 그 수준에 따라 언어체계가 완전히 달라야 한다는 말인가? 대상세계의 언어체계와 그 메타언어의 언어체계, 그 메타 메타 언어의 언어체계 등으로 계속되어야 하는가? 그렇다면 언어체계가 무한히 퇴행할 것이다. 그리하여 비트겐슈타인은 지붕에 오르기 위해 사다리를 사용하지만 오르고 나면 사다리가 필요없듯이, 특정한 수준에 이르면 언어를 초월해야 한다고 보았다. 이러한 무한퇴행을 피하려면 결국 자기를 지시하는 언어를 사용할 수밖에 없고 그렇게 되면 순환논법에 빠지게 된다. 이런 난문에 부딪쳐 자기지시적 언어를 사용하면서도 악순환 아닌 방법이 가능하다는 것을 보이고자 한 것이 카르납의 <언어의 논리적 구문론>이었다. 여기에서 카르납이 구성한 인공언어의 특색은 Kurt Gödel 의 산술화 방법을 자신의 구문론 서술에 사용한 것이다. 이에 의하면 구성언어 I 의 구문론을 기술하는 데 그와는 별도의 다른 언어를 사용한 것이 아니라 바로 언어 I 에 의해 언어 I 에 관한 구문론을 논했다. 그는 이를 근거로 비트겐슈타인처럼 무한퇴행이나 순환논법을 피하기 위해 말할 수 없는 그 어떤 것에 호소한다면 신비주의를 부활하는 것이라고 비판하였다.

이같은 카르납의 사상은 LISP의 회귀(recursion)표현에서 잘 드러난다. 전산과학의 가장 근본적인 개념중의 하나인 회귀개념은 LISP함수들이 작업을 수행하기 위해 하위 차원의 함수들을 호출하는 데도 쓰이고 LISP함수가 자기 자신을 직접 또는 간접으로 호출하는 데도 쓰이며 더욱이 함수가 자기를 정의하는 경우, 그 정의 안에서 자신을 호출하는 경우에도 쓰인다. 이 마지막 경우는 분명히 순환적이라고 할 수 있으나 그것은 악순환이 아니고 정당하게 자기를 정의하는 건전한 순환이라고 하겠다. 가령, LISP에서 리스트의 아톰들의 수 셀라(ATOM-SAELA)를 정의하는 경우 사용되는 회귀개념의 사례를 살펴 보면 건전한 순환이 가능함을 알 수 있다.

    * (DEFUN ATOM-SAELA (L)
    (COND ((NULL L) 0)                ; 아톰 없는 공리스트
           ((ATOM L) 1)                   ; 아톰 하나를 셈
       (T(+ (ATOM-SAELA (FIRST L)); 첫째 원소를 세고
           (ATOM-SAELA (REST L)))))    나머지 센 것을 더함.

이 회귀적 정의를 보면 피정의항(definiendum)인 ATOM-SAELA 가 정의항(definiens)인 COND 형 속에 두 번이나 들어 있으므로 분명히 정의할 것을 정의하는 속에서 사용하는 순환논법이지만 이 정의가 순환이라고 해서 잘못된 악순환이 아니라 정당한 정의임을 입증한 것이다. 이와 같은 형식의 정의로서 우리에게 친숙한 것은 수학에서 FACTORIAL을 정의한 것이다.

    n! = df 1 × 2 × 3 × ... × (n-1) × n

이 정의 방식을 LISP의 회귀적 표현으로 앞의 ATOM-SAELA를 틀에 맞추어 정의하면 다음과 같다.

    * (DEFUN FACTORIAL (N)
      (COND ((= N 0) 1)
      (T (* N(FACTORIAL (- N 1))))))

여기서도 FACTORIAL을 정의함에 있어서 FACTORIAL을 이용하는 자기지시적인 순환이지만 정당한 정의임이 밝혀진다. 이런 정의에 의해서 FACTORIAL의 가능함이 컴퓨터로 검토될 수 있기 때문이다. 즉 위와 같은 정의를 하고 나면 그 절차명을 사용하여 다음과 같이 그 정의한 절차 내용에 따라 일을 수행하게 할 수 있다.

    *(SETF E '(A B C D))
    (A B C D)
    *(ATOM-SAELA E)
    4
    *(FACTORIAL 5)
    120
    *(*1 2 3 4 5)
    120

우리는 지금까지 AI 프로그래밍 언어 LISP의 지극히 기초적인 개념들과 절차들을 소개하고 그들의 철학적인 연관성을 검토해 보았다. LISP의 기술적 측면에만 착안하고 그것의 실용성에만 집착하다 보면 철학적 연관성은 소홀해지기 쉽다. 그러나 인공언어 구성에는 철학적 전제들이 결부되므로 그 철학적 기초에 대한 음미없이 인공언어의 결점과 장점들을 종합적으로 검토하기는 어렵다. 또한 그런 검토없이 한층 더 높은 단계의 언어를 구성한다는 것은 기대할 수도 없을 것이다. 뿐만 아니라 LISP의 회귀 표현 같은 것에서 보았듯이 AI의 언어를 통해 역으로 철학적 난제 해결의 가능성을 발견하게 되며 이런 문제는 AI 활용의 철학이 직면한 과제이다.