U E D R , A S I H C RSS

이민석/리스프 (rev. 1.18)

이민석/리스프

A Gentle Introduction To Symbolic Computation: http://www.cs.cmu.edu/~dst/LispBook/
읽으면서 정리해보자

서문

  • 리스프는 인공지능 연구의 주요 언어로 유명하다
  • 염두에 둔 독자: 프로그래밍 입문하는 학생 / 심리학자, 언어학자, 컴퓨터 과학자 등 인공지능에 관심 있는 사람들 / 취미로 컴퓨터 하는 사람
  • 책의 구성
    • 1, 2장: 상자, 화살표 표기로 기초적인 함수, 함수 합성 설명
    • 3장: EVAL 표기
    • 8장까지는 부수효과 없는 프로그래밍
    • 9장: 입출력
    • 10장: ordinary variables, generalized variables, destructive sequence operations.
    • 11장: 반복(DO, DO*)
    • 12장: 구조
    • 13장: 배열, 해시테이블, property list
    • 14장: 매크로, 컴파일, lexical scoping과 dynamic scoping의 차이
  • 간략화
    • Common Lisp는 복잡한 언어라 적당히 간략화한 것들이 있다
    • 1+와 1-는 이름이 혼란스러워 뺐다
    • EQUAL을 주로 사용. EQ, EQL, EQUALP, =는 고급 주제에서 논의
    • 잘 알려지지 않은 PUSHNEW 같은 원시형을 사용하느니 함수를 좀 더 풀어쓴 것이 몇 군데 있다
    • 가장 고급 주제인 multiple value나 package system은 다루지 않는다

PDF 파일에 목차가 없어 ㅡㅡ

1장. 함수와 데이터

1장 요약

  • 산술 함수: +, -, *, /, ABS, SQRT
  • 숫자형: 정수(integer), 부동소수점수(floating number), 비율수(ratio)
    • 정수로 산술 연산을 하면 결과는 정수 또는 비율수다.
    • 3/6 = 1/2 <- 이게 ratio
    • 3/6.0 = 0.5
  • 심볼: 숫자 이외의 또다른 자료형
    • 특수 심볼: T는 참 또는 긍정, NIL은 거짓 또는 부정
    • T 또는 NIL을 반환하는 함수는 술어식(predicate)
  • 기본적인 내장 함수들
    • NUMBERP: 데이터가 숫자형인가?
    • SYMBOLP: 데이터가 심볼인가?
    • 숫자형 전용 술어식들: ZEROP(영), ODDP(홀수), EVENP(짝수)
    • 크기 비교: <. >
    • 항등 비교: EQUAL
    • 내장 함수들은 원시 함수(primitive function) 또는 그냥 primitive라고 부른다
    • 입력에서 1을 더하거나 빼는 1+, 1- 함수가 있지만 이 책에선 쓰지 않겠다. 헷갈림
    • 부정: NOT(NIL은 T로, NIL이 아닌 모든 것은 NIL으로)
      • 그럼 (NOT (NOT 5))는 5가 아니라 T인가? => T 맞음
  • 함수의 인자 개수
    • ODDP의 인자는 딱 1개, EQUAL은 2개
    • +, -, *, /의 인자 개수는 여러 개
      • +의 인자가 2, 3, 4면 2랑 3부터 더하고 여기에 4를 더한다
      • -, /의 인자가 하나인 경우
        • -의 인자가 n이면 결과는 0에서 n 뺀 거
        • /의 인자가 n이면 결과는 1에서 n 나눈 거
      • 타 언어는 산술 연산이 이항 연산이고 2+3+4는 단순히 (2+3)+4로 처리되는데
      • 리스프는 + 자체가 n항 연산이 될 수 있다는 건가???
  • 오류: 3이랑 FRED를 더할 수 없고, EQUAL의 인자를 한 개만 넘기면 안 되고, 0으로 나누면 안 되고.

1장 고급 주제

  • 리스프의 역사
    • 1956년 Dartmouth 대학에서 여름에 열린 인공지능 관련 연구 모임에서 존 매카시가 "list processing"이란 기법을 배웠다
      • 1950년대에는 어셈블리어로 프로그래밍을 했지
      • "list processing"을 발표한 사람들은 심볼과 리스트를 다루는 보다 추상적인 IPL이란 언어를 만들었다
      • 그 문법이 어셈블리어에 가까워서 괴상했다
    • 한편 수치적 계산에 특화된 FORTRAN이 개발되고 있었다
      • 어셈블리어는 Y = (X + 5) * 10 하려면 LOAD Y, X -> ADD Y, 5 -> MULT Y, 10 라고 써야 하는데
      • FORTRAN은 그냥 Y = (X + 5) * 10 이라고 쓰면 된다. 표현식(expression)의 작성이 가능하다는 뜻
      • 그 당시에는 이 개념이 혁명이였다더라
      • 존 매카시: 나도... 나도 이런 거 만들 거야!
        • FORTRAN에 리스트 조작을 위한 특별한 하위루틴들을 추가하면 어떨까?
        • IBM의 Herbert Gelerntner와 Carl Gerberich가 이 아이디어를 따와 FLPL을 만듦
        • 매카시는 IPL, FORTRAN, FLPL을 토대로 LISP를 설계
    • Lisp 1.5는 처음으로 널리 퍼진 리스프 방언
    • 1960년대 중반부터 온갖 리스프 방언이 생기기 시작
      • MacLisp, Interlisp, Stanford Lisp 1.6, UCI Lisp...
      • 모두 Lisp 1.5을 확장한 것. 하지만 서로 호환이 하나도 안 된다
    • 1970년대: ALGOL계 언어의 특징들과 리스프의 문법을 결합한 Scheme이 나왔어요
      • 그리고 또다시 Scheme의 방언들이 우후죽순 생겨나기 시작했다
    • 1980년대: 널리 쓰이는 리스프 방언만 해도 수두룩한데... 뭘 써야 하지?
      • 만국공용어를 만들자!!!
      • 1984년 Common Lisp 초안 발표
      • 학술계에서도 산업계에서도 빠르게 주류로 성장
      • 지금은 Common Lisp 때문에 Scheme 빼고 거진 다 죽었지렁
  • 많은 프로그래밍 아이디어가 리스프에서 출발할 것
    • 인터프리터 함수와 컴파일 함수의 결합
    • 함수 재귀 호출
    • 소스 수준 추적 & 디버깅
    • 문법 지향 편집기
    • 오늘날의 리스프는 함수형, 객체지향, 병렬 프로그래밍 연구의 선두 주자

2장. 리스트

2장 요약

  • Lisp는 List Processor라는 뜻
  • 리스트는 가장 다재다능한(versatile) 타입이다
    • 집합, 테이블, 그래프, 영어 문장 등 뭐든지 표현 가능
    • 함수도 리스트로 표현 가능
  • 모든 리스트는 두 가지 형태를 가진다
    • printed representation
      • 사람이 키보드로 쓰기 편한 형식
    • internal representation
      • 실제로 메모리에 거주하는 형식
    • 리스트의 예: (RED GREEN BLUE), (AARDVARK), (2 3 5 7 11 13 17)
    • 메모리에서의 실제 형태: 셀(cell)마다 원소를 가리키는 포인터, 다음 셀을 가리키는 포인터를 가진다
    • 마지막 셀은 NIL을 가리킨다
      Figure_2.1.png
      [PNG image (5.19 KB)]

    • 포인터는 대개 4바이트이므로 셀은 8바이트
    • 중첩 리스트
      • ((BLUE SKY) (GREEN GRASS) (BROWN EARTH))
        Figure_2.2.png
        [PNG image (13.3 KB)]

    • 리스트의 길이: 원시 함수 LENGTH를 이용
    • 빈 리스트는 NIL로 표현
    • 사실 NIL은 ()다. NIL과 빈 리스트는 동치
    • NIL은 심볼이자 리스트인 유일한 존재
    • 리스트의 원소 얻기
      • FIRST, SECOND, THIRD 함수: 첫 번째, 두 번째, 세 번째 원소
      • REST 함수: 첫 번째를 제외한 나머지 리스트
      • FIRST는 CAR와 같고 REST는 CDR과 같다.
        • CAR, CDR이라는 이름은 리스프가 처음에 트랜지스터도 없어서 진공관 쓰는 컴퓨터에서 작동할 때 쓴 말
        • CAR: Contents of Address portion of Register
        • CDR: Contents of Decrement portion of Register (카우더cou-der라고 발음)
        • 요즘 컴퓨터에는 맞지 않지만 아직도 쓴단다
        • CADR: CDR 다음에 CAR (kae-der라고 발음)
        • CDAR: CAR 다음에 CDR (cou-dar)
        • CADDR: CDR 다음에 CDR 다음에 CAR (ka-dhi-der)
        • 이게 뭐야 ㅡㅡ
          Figure_2.3.png
          [PNG image (46.71 KB)]

        • CAR와 CDR에 NIL을 입력하면 NIL이 출력된다
          • 에러가 아니구요???
          • 이게 더 좋대
    • 리스트 생성
      • CONS는 한 데이터와 리스트를 받아서 그 데이터를 첫 번째 원소로 끼워넣은 리스트를 반환한다
      • x = CONS of (CAR of x) and (CDR of x)
      • LIST는 임의 개수의 원소를 받아 그것들의 리스트를 생성한다
    • 리스트 술어식
      • LISTP: 입력이 리스트인가?
      • CONSP: 입력이 cons cell인가?
        • LISTP와 비슷하지만, NIL은 리스트인 반면 cons cell이 아니다
      • ATOM: 입력이 cons cell이 아닌가? (CONSP의 부정)
      • NULL: 입력이 NIL이면 T 반환. NOT과 동치.
        • 논리 연산에는 NOT을, 리스트 연산에는 NULL을 사용하는 관례가 있다

2장 고급 주제

  • 리스트를 이용한 1진법 산술
    • 0 = NIL, (X) = 1, (X X) = 2, (X X X) = 3...
    • REST는 1 빼기. 단 0 빼기 1은 0
    • LENGTH는 실제 숫자로 변환
    • 이걸 어따 써먹죠
  • 진리스트(proper list)는 NIL로 끝나는 리스트
    • NIL로 안 끝나는 리스트는? dotted list
    • A와 B의 CONS : (A . B) <- dotted pair
    • LIST는 진리스트만 생성 가능. CONS는 Dotted list를 만들 수 있다
  • 순환 리스트(circular list)도 있다
    • 원소 A, B, C가 있는데 C를 포함하는 cell이 다시 A를 포함하는 cell을 가리키면?
    • sharp-equal notation: #1=(A B C . #1#) 라고 표기한다
  • (A B C . D)의 LENGTH는 4가 아니라 3
  • 순환 리스트의 LENGTH는 무한 루프

하루에 한 챕터씩 읽을려고 했는데 이상한 거 코딩하다가 못 했네 ㅡㅡ 3장은 내일

3장 EVAL 표기

3장 요약

  • 함수를 그림으로 그리지 말고 리스트로 표현하자
  • 리스프에서 함수는 데이터다
  • EVAL 표기를 정복했으면 리스프를 통해 컴퓨터와 대화하는 데 필요한 건 대부분 알게 된 셈
  • EVAL 함수는 리스프의 핵심이다
    • 리스프 표현식을 평가해서 결과값을 내놓는다
    • 함수 뒤에 입력값들이 따라오는 형식
    • 표현식 (+ 2 3)는 5로 평가된다
    • (+ 1 6) => 7
    • (oddp (+ 1 6)) => t
    • (* 3 (+ 1 6)) => 21
    • (/ (* 2 11) (+ 1 6)) => 22/7
  • EVAL의 동작을 정의하는 평가 규칙들
    • 숫자형, T, NIL은 그 자신으로 평가된다
    • 리스트의 첫 원소는 함수, 나머지는 그 함수에 전달되는 아직 평가되지 않은 인자다
      • 인자들은 왼쪽에서 오른쪽으로 평가된다
    • 심볼은 그 심볼이 가리키는 변수의 값으로 평가된다
    • (ODDP (+ 1 6))의 evaltrace diagram(평가추적도표?)
      Figure_3.1_evaltrace_diagram.png
      [PNG image (12.47 KB)]

  • EVAL 표기로 함수 정의하기
    • 두 수의 평균을 구하는 AVERAGE 함수: (defun average (x y) (/ (+ x y) 2.0))
      • defun은 매크로 함수로서 그 인자를 평가하지 않는다
      • defun은 함수를 정의하기 위한 함수다
        • 첫 번째 인자는 함수 이름
        • 두 번째 인자는 인자 목록
        • 세 번째 인자는 함수 몸체
      • 이제 (AVERAGE 6 8) 처럼 쓸 수 있다
    • T와 NIL을 제외한 거의 모든 심볼을 인자 이름으로 쓸 수 있다
  • 변수: 데이터가 저장되는 공간
    • (defun average (x y) (/ (+ x y) 2.0))에서 x와 y가 변수
    • 변수는 심볼이 아니다
    • 심볼을 가지고 변수에 이름을 붙인 것
      • 리스프 프로그래머들이 "어떤 변수가 어떤 값으로 평가된다"고 말할 때
      • 사실은 "심볼이 그 심볼이 가리키는 변수의 값으로 평가된다"고 말하는 것
    • average란 심볼을 가지고 함수에 이름을 붙인 것
    • x, y 변수는 average 함수 안에서만 사용 가능
      • 머 흔히 말하는 스코프 개념이겠지
    • 전역 변수: 어떤 함수와도 엮이지 않은 변수
      • 예: PI = 3.14159
    • 값이 할당되지 않은 변수의 값을 요구하면 "미할당 변수 오류(unassigned variable error)"가 발생한다
      • "unbound variable error"라고 하는 사람들도 있지만 이건 역사적인 용어라 Common Lisp에 맞지 않음
      • 그럼 CAR니 CDR은 왜 쓰는 건데 ㅡㅡ
      • Common Lisp에는 EGGPLANT라는 내장 변수가 없으며 EGGPLANT 심볼을 평가하면 오류가 발생한다
      • 심볼이 왜 자꾸 문단에선 대문자로 나왔다 코드에선 소문자로 나왔다 하는 거야 뭐가 맞는 거야
  • 심볼과 리스트를 데이터로 활용하기
    • 심볼 KIRK과 SPOCK이 같은 지 비교하고 싶어요
    • (equal kirk spock) 라고 쓰면 되지
    • 아니, KIRK이라는 심볼과 SPOCK이라는 심볼 자체를 비교하고 싶다니까요
    • 아ㅋ (equal 'kirk 'spock) 따옴표를 붙여
      • T와 NIL은 그 자신으로 평가되기 때문에 따옴표를 붙일 필요가 없다
    • Quoted Object의 평가 규칙: 따옴표를 뗀 자신으로 평가된다
    • (third (my aunt mary)) => Error! MY undefined function.
      (third '(my aunt mary)) => mary
    • 그러니까 따옴표는 평가되는 걸 막는다는 뜻인가
    • 리스트를 만드는 세 가지 방법
      • '(foo bar baz) => (foo bar baz)
      • (list 'foo 'bar 'baz) Þ (foo bar baz)
      • (cons 'foo '(bar baz)) Þ (foo bar baz)
    • 이런 건 오류
      • (list foo bar baz) => Error! FOO unassigned variable.
      • (foo bar baz) => Error! FOO undefined function.
      • ('foo 'bar 'baz) => Error! 'FOO undefined function.
      • 슬슬 멘붕온다 겁나 헷갈린다
  • READ-EVAL-PRINT LOOP(REPL)
    • 그냥 콘솔에 뭐 치면 읽고 평가하고 출력 많이 하던 거네
  • 리스프 프로그래밍 환경
    • 코드 에디터: 괄호 오류 잘 찾아주는 거
      • 딱 봐도 괄호 땜에 오류 겁나 쏟아지게 생길 언어네

3장 고급 주제

  • 인자 없는 함수
    • 85에 97을 곱하는 함수를 정의하고 싶은데요
      • (defun test () (* 85 97))
  • 특수 함수 QUOTE
    • QUOTE의 인자는 평가되지 않는다
    • (quote foo) => foo
    • 따옴표랑 같은 거 아냐
      • (cons 'up '(down sideways))
      • (cons (quote up) (quote (down sideways)))
  • 심볼의 내부 구조
    • CONS 심볼은 자신의 function cell에 함수 포인터를 가진다
      Figure_3.2_Symbol_Internal_Structure.png
      [PNG image (11.92 KB)]

    • (EQUAL 3 5)를 cons cell 연쇄로 표현
      Figure_3.3_EQUAL_cons_cell_chain.png
      [PNG image (5.05 KB)]

    • 더 자세히 표현하면?
      Figure_3.4_EQUAL_cons_cell_chain_detailed.png
      [PNG image (16.98 KB)]

    • 내장 함수 SYMBOL-NAME과 SYMBOL-FUNCTION
  • 람다 표기
    • 프린스턴 대학의 수학 교수 처치가 창안
    • 리스프의 원작자 존 매카시는 처치의 학생이여따
    • x + 3을 람다로 표현하면 (lambda (x) (+ 3 x))
    • DEFUN이랑 비슷한데?
      • LAMBDA는 함수가 아니다 --뭐라구요
      • EVAL이 특별 취급하는 마커marker
    • DEFUN의 역할은 이름과 함수를 엮어주는 것
      • HALF라는 새로운 함수를 정의할 때
        • 문자열 "HALF"는 심볼의 이름
        • 심볼 HALF는 함수의 이름
          Figure_3.5_LAMBDA_STRUCTURE.png
          [PNG image (35.53 KB)]

  • 원시 함수 EVAL
    • (eval '(+ 2 2)) => 4
    • '(list '* 9 6)) => (list '* 9 6)
      (eval '(list '* 9 6)) => (* 9 6)
      (eval (eval '(list '* 9 6))) => 54
  • 원시 함수 APPLY
    • 함수와 인자 목록을 인자로 취해서
    • 그 인자 목록을 가지고 함수를 호출한다
    • (apply #'+ '(2 3)) => 5
      • 함수를 다른 함수의 인자로 넘길 때는 '가 아니라 #'를 써야 한다
      • 7장에서 알려줄게

4장. 조건문

4장 요약

  • 조건문은 모두 특수 함수 또는 매크로이기 때문에 인자들이 자동 평가되지 않는다
    • 3장에서 본 DEFUN, QUOTE 함수도 그런 성질을 가진다
    • +, CONS 같은 함수는 항상 인자를 평가한다
  • 특수 함수 IF
    • 문법: (if (test) (true-part) (false-part))
    • 절댓값 함수: (defun my-abs (x) (if (< x 0) (- x) x))
    • 세 번째 인자 즉 false-part는 생략할 수 있다. 이 경우 NIL로 처리된다.
  • COND 매크로
    • 간략한 형태
      (COND (test-1 consequent-1)
      (test-2 consequent-2)
      (test-3 consequent-3)
      ....
      (test-n consequent-n))
    • test-1이 참이면 consequent-1로 평가됨
    • 아니면 test-2를 확인해보고 참이면 consequent-2로 평가됨
    • ... 그렇게 test-n까지 반복
    • test-n까지 거짓이면 NIL로 평가됨
    • (defun compare (x y) (cond ((equal x y) 'numbers-are-the-same) ((< x y) 'first-is-smaller) ((> x y) 'first-is-bigger)))
    • test-n에 T를 넣으면 consequent-n이 무조건 실행됨을 보장
    • (IF test true-part false-part) = (COND (test true-part) (T false-part))
  • AND 매크로와 OR 매크로
    • (and clause-1 clause-2 ... clause-n)
      • clause-1이 NIL이면 NIL로 종료. 아니면 계속
      • clause-2이 NIL이면 NIL로 종료. 아니면 계속...
      • clause-n이 NIL이면 NIL로 종료. 아니면 clause-n의 값 반환
      • (and 1 2 3 4 5) => 5
    • (or clause-1 clause-2 ... clause-n)
      • clause-1이 NIL이 아니면 clause-1의 값 반환. NIL이면 계속
      • clause-2이 NIL이 아니면 clause-2의 값 반환. NIL이면 계속
      • ...
      • clause-n이 NIL이 아니면 clause-n의 값 반환. NIL이면 NIL 반환
      • (or 'george 'fred 'harry) => george
      • (or nil 'fred 'harry) => fred
    • clause-x에서 평가가 끝나면 그 뒤의 clause들은 평가되지 않는다
      • (defun posnump (x) (and (numberp x) (plusp x)))
      • (numberp x)가 NIL이면 plusp는 실행되지 않는다
      • 만약 부수효과가 있는 코드라면... 신중해야 할 것
      • 리스프에서 부수효과를 어떻게 일으키는지 아직은 모르지만
      • 참고로 PLUSP는 숫자가 양수인지 확인하는 술어식
  • 리스프 도구: STEP
    • 리스프 표현식의 평가 과정을 단계별로 보여준다. 디버깅용

4장 고급 주제

  • 불리언 함수
    • (defun logical-and (x y) (and x y t))
      • (logical-and 'tweet 'woof) => t
      • (and 'tweet 'woof) => woof
  • 드 모르강의 법칙
    • (and x y) = (not (or (not x) (not y)))
    • (or x y) = (not (and (not x) (not y)))
    • 난 법칙이라고 배웠는데 영어로는 theorem이네... theorem은 정리 아닌감
    • T, NIL에 대한 논리 연산일 때만 성립
      • (not (not fred))는 fred가 아니라 T다

5장. 변수와 부수효과

5장 요약

  • 변수는 어떻게 생성되고 그 값은 어떻게 변해가는가?
  • '''부수효과side effect: 함수가 값을 반환하는 것 외에도 추가로 취하는 행동action
    • 변수의 값을 바꾸는 것은 부수효과의 일종
  • 모든 변수는 스코프scope를 가진다
    • 함수 몸체 내의 변수들은 지역 변수local variable
  • 매크로 함수 SETF: 변수에 값을 할당한다
    • (setf vowels '(a e i o u))
    • (setf vowels '(a e i o u and sometimes y))
    • 덮어쓰기 가능. 순수성 같은 건 없다
  • SETF의 부수효과: 변수의 값을 변경한다
  • DEFUN의 부수효과: 새로운 함수를 정의한다
  • RANDOM의 부수효과: 임의의 값을 반환한다
    • (random 5) => 3
    • (random 5.0) => 2.78123
    • 뭐가 부수효과인데요 => 의사난수 발생기에 사용하는 숨겨진 변수의 값을 변경한다
    • 그럼으로써 매번 다른 결과를 반환하는 것
  • SETF는 지역 변수든 전역 변수든 가리지 않는다
    • 하지만 전역 변수에만 쓰는 것이 좋은 실천이다 => 왜?
  • 특수 함수 LET
    • 지역 변수를 만드는 또다른 방법
      (defun average (x y)
      (let ((sum (+ x y)))
      (list x y ’average ’is (/ sum 2.0))))
  • 특수 함수 LET*
    • 지역 변수들을 한 번에 하나씩 만든다
    • 첫 번째 지역 변수가 lexical context를 생성하고 그 context 안에서 두 번째 변수의 값이 계산되고... 머라고요?
    • 긴 계산에서 중간 결과들에 이름을 부여하고 싶을 때 유용하다
    • 그러니까 LET은 변수 a, b, c를 정의할 때 b, c에서 a를 이용할 수 없고 LET*은 그게 된다는 말인가?
  • 그럼 LET*만 쓰면 되지???
    • LET을 쓸 수 있으면 LET을 써라
    • 변수 사이에 의존성이 없음을 보장
  • 리스프 도구모음: DOCUMENTATION과 APROPOS
    • 대부분의 리스프 구현은 모든 내장 함수와 변수의 온라인 문서를 포함한다
      • DOCUMENTATION 함수는 문서 문자열을 반환한다
      • (documentation ’cons ’function)
      • (defun average (x y) "Returns the mean (average value) of its two inputs." (/ (+ x y) 2.0))
        • 직접 함수를 정의할 때는 인자 목록 바로 뒤에 써주면 된다
        • 아니면 주석comment을 사용하라
          • ;로 시작하는 줄은 주석
          • 관례: 줄 끝에 쓰면 ;, 함수 안에서 한 줄을 차지하면 ;;, 함수 밖에서는 ;;;
    • APROPOS: 그거 이름이 정확히 뭐더라...
      • 특정 문자열을 포함하는 모든 심볼을 반환한다
      • (apropos "TOTAL" "USER")
        ARRAY-TOTAL-SIZE (function)
        ARRAY-TOTAL-SIZE-LIMIT, constant, value: 134217727
      • APROPOS의 두 번째 인자는 패키지 이름. 항상 "USER"를 쓸 것
      • 패키지는 Common Lisp의 이해하기 어려운 특징들 중 하나. 이 책에선 다루지 않는다

5장 고급 주제

  • 심볼의 내부 구조
    • 심볼의 내부는 다섯 개의 셀로 구성된다
    • 전역 변수 TOTAL의 값이 12일 때 TOTAL 심볼의 내부 구조
      Figure_5.1.png
      [PNG image (4.59 KB)]

  • T와 NIL의 경우
    Figure_5.2.png
    [PNG image (9.39 KB)]

  • 하나의 심볼으로 여러 변수에 이름을 부여할 수 있다
    • 하지만 그 중 오직 한 변수만 전역 변수가 될 수 있다
    • 값 셀은 그 전역 변수를 위해 예약된다
    • 다른 모든 변수들은 local context 안에 있어야 한다
    • 그리고 그 변수들의 값은 이 심볼의 값 셀이 아닌 다른 데에 저장되어야 한다
    • 어디에 저장될 지는 표준에 정의되어 있지 않다. 그걸 컴파일러 작성자 너님이 해결해야 함 ㅋ
  • 근데 심볼에 셀이 5개라고???
    • 하나의 심볼에 값도 있고 함수 포인터도 있고
      Figure_5.3.png
      [PNG image (13.7 KB)]

    • (CAR '(A B C)) 는 CAR 함수를 호출한다
    • (LIST 'A 'NEW CAR) 는 전역 변수 CAR를 참조한다
  • 바인딩, scoping, 할당
    • Common Lisp는 오래된 리스프 방언들로부터 진화해왔기 때문에 역사적인 이유로 용어를 혼용한다
    • 바인딩: 새로운 변수를 생성하고 값을 주는 과정
      • 함수의 인자 목록에 변수가 나타나면: 람다 바인딩
      • LET이나 LET*의 변수 목록에 나타나면: let 바인딩
    • 그런데 예전부터 리스프를 써온 사람들은 좀 다르게 말한다...
      • variables are lexically scoped by default 이걸 뭐라고 번역해야 매끄러우려나 ㅡㅡ
      • Common Lisp는 동적 바인딩도 지원한다
      • 초기 리스프 방언들은 대개 동적 바인딩이 기본이였다
      • dynamically scoped variable들은 "bound"가 반드시 값을 가졌다는 뜻이 아니다

6장. 리스트 자료구조

6장 요약

  • 리스프는 연결 리스트, 심볼릭 자료구조, 저장공간 할당기 등을 기본적으로 제공
  • 리스트는 일방통행 연쇄다
    • (cons 'w '(x y z)) => (w x y z)
    • (cons '(a b c) 'd) => ((a b c) . d)
  • 리스트 연결 함수 APPEND
    • (append '(friends romans) '(and countrymen))
      => (FRIENDS ROMANS AND COUNTRYMEN)
  • CONS, LIST, APPEND의 차이점
    • CONS는 새로운 cons cell을 생성한다. 리스트의 머리에 새 원소를 추가할 때 쓰인다
    • LIST는 임의 개수의 입력을 받아서 NIL로 끝나는 cons cell 연쇄를 만든다. 각 cell의 car는 대응하는 입력을 가리킨다.
    • APPEND는 첫 번째 입력을 복사해서 마지막 셀의 cdr이 두 번째 입력을 가리키도록 한다. 첫 번째 입력값이 리스트가 아니면 오류.
  • 지금까지 본 리스트 함수들: CONS, LIST, APPEND, LENGTH
  • 아직 많이 남았다: REVERSE, NTH, NTHCDR, LAST, REMOVE
    • REVERSE: 원래 리스트는 놔두고 뒤집힌 복사본을 반환한다
    • NTHCDR: n번째 CDR
      • (nthcdr 0 ’(a b c)) => (a b c)
      • (nthcdr 2 ’(a b c)) => (c)
      • (nthcdr 3 ’(a b c)) => nil
      • (nthcdr 5 ’(a b c)) => nil ; 리스트 길이 넘어가도 오류 아님
        • 단 dotted list인 경우엔 오류
    • NTH: (defun nth (n x) "Returns the Nth element of the list X, counting from 0." (car (nthcdr n x)))
    • LAST: 마지막 cons cell을 반환한다
      • (last '(all is forgiven)) => (forgiven)
      • (last '(a b c . d)) => (c . d)
    • REMOVE: 일치하는 모든 항목 제거
      • (remove 'a '(b a n a n a)) => (b n n)
      • 원래 리스트는 놔두고 수정된 사본을 반환한다
  • 집합(set)으로서의 리스트
    • 집합에서는 어떤 원소는 최대 한 번만 출현할 수 있다
    • 가능한 연산: 원소 여부, 합집합, 교집합, 차집합, 부분집합 여부
    • MEMBER: 일치하는 원소로 시작하는 부분 리스트 반환. 없으면 NIL 반환.
      • (defun beforep (x y l) "Returns true if X appears before Y in L" (member y (member x l)))
    • INTERSECTION: 교집합
    • UNION: 합집합
    • SET-DIFFERENCE: 차집합
    • SUBSETP: 부분집합인가?
      • (subsetp '(a i) '(a e i o u)) => t
  • 테이블로서의 리스트
    • ASSOC 함수
      • (setf words '((one un) (two deux) (three trois) (four quatre) (five cinq)))
      • (assoc 'three words) => (three trois)
    • RASSOC 함수
      • 테이블이 dotted pair의 리스트여야 한다
      • (setf sounds '((cow . moo) (pig . oink) (cat . meow) (dog . woof) (bird . tweet)))
      • (rassoc 'woof sounds) => (dog . woof)
      • (assoc 'dog sounds) => (dog . woof)

6장 고급 주제

  • 트리
    • SUBST 함수
      • (subst x y z) => z 안에 있는 모든 y를 x로 대체한다
      • 리스트를 재귀적으로 탐색, 모든 y를 x로 대체
    • SUBLIS 함수
      • 여러 SUBST를 한꺼번에
      • (sublis '((roses . violets) (red . blue)) '(roses are red))
        => (VIOLETS ARE BLUE)
  • 공유 구조(shared structure)
    • 두 리스트가 하나의 하위 구조를 공유한다
    • 콘솔에서는 항상 리스트의 복사본을 만들기 때문에 이런 거 못 만든다
    • CAR, CDR, CONS로 생성 가능
    • (setf x ’(a b c))
      (setf y (cons ’d (cdr x)))
      Figure_6.1.png
      [PNG image (9.72 KB)]

  • 항등 검사
    • EQUAL은 두 리스트의 모든 원소를 짝지어본다 (값의 비교)
    • EQ는 두 리스트가 같은 주소를 참조하는지를 본다 (참조의 비교)
    • EQL은 EQ와 동일, 단 숫자형의 경우 그 값을 비교
      • 타입이 다르면 같지 않은 걸로 처리 (eql 3 3.0) => nil Different types.
    • =는 숫자 비교할 때만 사용
    • EQUALP는 EQUAL과 동일하나 대소문자를 구분하지 않음 (equalp "foo bar" "Foo BAR") => t
    • 아 이게 다 뭐야 ㅡㅡ
  • 키워드 인자
    • 많은 리스프 함수가 키워드 인자keyword argument라는 추가 인자를 가진다
    • REMOVE의 기본 동작은 발견된 모든 원소 삭제
      • 몇 개를 삭제할 지를 지정할 수 있다
        (setf text '(b a n a n a - p a n d a))
        (remove 'a text :count 3)
      • 뒤에서부터 지우는 것도 가능
        (remove 'a text :count 2 :from-end t)
    • 키워드는 항상 :가 붙는 특별한 심볼
      • COUNT와 :COUNT는 별개의 심볼
      • 키워드는 항상 자기 자신으로 평가된다
      • KEYWORDP: 입력이 키워드인가?
    • MEMBER는 리스트에서 원소를 찾을 때 EQL을 사용
      • (setf cards '((3 clubs) (5 diamonds) (ace spades)))
      • (member ’(5 diamonds) cards) => nil 으잉 ㅜㅜ
      • (member ’(5 diamonds) cards :test #’equal)
        => ((5 DIAMONDS) (ACE SPADES))
      • :TEST 키워드를 허용하는 함수들: UNION, INTERSECTION, SET-DIFFERENCE, ASSOC, RASSOC, SUBST, SUBLIS

7장. Applicative Programming

7장 요약

  • Applicative를 뭐라고 번역해야 할까? 하스켈의 Applicative Functor도 이 applicative에서 나온 용어인데 '적용성 작용자'쯤으로 옮기면 의미가 얼추 맞지만 applicative functor나 적용성 작용자나 처음 보는 사람이 이해할 리가.. -.- 일단 '함수를 apply하다'에서 나온 말인데 이 apply는 '응용하다'가 아닌 '뭐를 뭐에 적용하다'로 쓰인 것이다.
  • 프로그래밍의 세 가지 유형: applicative, recursion, iteration
  • applicative programming의 기본 발상: 심볼은 데이터. 리스트는 데이터. 그러면 함수도 데이터.
    • 함수를 다른 함수에 인자로 전달할 수 있다
    • 함수가 어떤 함수를 반환할 수 있다
    • 원시 함수 FUNCALL
      • (funcall #’cons 'a 'b) => (a . b)
    • 일반 함수(ordinary function)를 quote하려면 #'를 붙인다
      • 매크로 함수, 특수 함수는 불가함
    • MAPCAR 연산자: 리스트의 각 원소에 함수를 적용하고 그 결과들을 반환
      • 어떤 언어에든 흔히 있는 배열용 map 함수
      • (defun square (n) (* n n))
        (mapcar #’square '(1 2 3 4 5))
        (mapcar #’square '()) => nil
      • 람다 버전: (mapcar #'(lambda (n) (* n n)) '(1 2 3 4 5))
        • 람다는 매크로 함수나 특수 함수가 아니라 이름 없는 일반 함수
    • FIND-IF 연산자: 조건 맞는 첫 번째 원소
      • (find-if #'oddp '(2 4 6 7 8 9))
    • REMOVE-IF: 조건 맞는 모든 원소 제거하고 남은 리스트
      • (remove-if #'numberp '(2 for 1 sale)) => (2 1)
    • REMOVE-IF-NOT: 조건 맞는 원소들만 모아서 반환
    • REDUCE 연산자: 하스켈의 fold
      • (reduce #’+ '(1 2 3)) => 6
    • EVERY: 모든 원소가 조건에 맞는가? (every #'numberp '(1 2 3 4 5))
    • 리스프 도구모음: TRACE와 DTRACE

7장 고급 주제

  • MAPCAR는 여러 리스트를 한꺼번에 처리할 수 있다
    • 리스트 길이가 다르면? 제일 짧은 리스트가 끝날 때 짤라먹음
  • 특수 함수 FUNCTION: '가 특수 함수 QUOTE의 단축 표현이듯 사실 #'는 FUNCTION의 단축 표현
    • QUOTE는 인자를 평가하지 않고 반환한다
    • FUNCTION은 인자의 함수 측면에서의 해석 결과를 반환한다
      • 인자가 심볼이면 심볼의 함수 셀의 내용물(컴파일된 코드 객체)
      • 인자가 람다면 lexical closure
  • applicative 연산자의 키워드 인자
    • (find-if #'oddp '(2 3 4 5 6) :from-end t)
    • (reduce #'cons '(a b c d e)) => ((((A . B) . C) . D) . E)
    • (reduce #'cons '(a b c d e) :from-end t) => (A B C D . E)
  • 함수를 반환하는 함수
    • (defun make-greater-than-predicate (n) #'(lambda (x) (> x n)))

8장. 재귀

8장 요약

  • 주요 주제: 재귀적인 제어 구조
  • 하나라도 홀수인가?

(defun anyoddp (x)
    (cond ((null x) nil)
          ((oddp (first x)) t)
          (t (anyoddp (rest x)))))
  • 너무 코딩 안 해보고 책만 읽는 것 같다. 코드 안 보고 팩토리얼 짜보자
    • 성공은 했는데... 괄호 개많어 ㅡㅡ

(defun factorial (n) (if (zerop n) 1 (* n (factorial (- n 1)))))
  • 재귀 템플릿: 너의 재귀 패턴은 강약강약약중강약이다
    • Double-Test Tail Recursion

(DEFUN func (X)
  (COND (end-test-1 end-value-1)
        (end-test-2 end-value-2)
        (T (func reduced-x))))
  • Single-Test Tail Recursion

(DEFUN func (X)
  (COND (end-test end-value)
        (T (func reduced-x))))
  • Augmenting Recursion
    • f(n)에서 f(n-1)에 모종의 연산을 적용

(DEFUN func (X)
  (COND (end-test end-value)
        (T (aug-fun aug-val
        (func reduced-x)))))
  • 트리와 CAR/CDR 재귀
    • 중첩 리스트의 모든 원소 처리하기
      Figure_8.1.png
      [PNG image (11.44 KB)]


(defun find-number (x)
  (cond ((numberp x) x)
        ((atom x) nil)
        (t (or (find-number (car x))
               (find-number (cdr x))))))

8장 고급 주제

  • 꼬리 재귀의 이점
    • 다른 재귀보다 효율적으로 실행
  • 특수 함수 LABELS
    • LET은 지역 변수
    • LABELS는 지역 함수

(LABELS ((fn-1 args-1 body-1)
         ...
         (fn-n args-2 body-2))
  body)
  • 재귀 자료 구조
    • S-표현식(symbolic expression): 아톰, 또는 CAR와 CDR이 S-표현식인 cons cell
    • 트리는 단일 말단 노드이거나 가지들이 트리인 비단말 노드다.

9장. 입출력

9장 요약

  • 리스프에선 입출력 인터페이스가 아직도 표준화가 안 됐다
    • 그나마 아주 기본적인 입출력 루틴들은 표준화가 되었음
  • 문자열(character string): vector의 subtype
    • 문자열은 그 자신으로 평가된다
    • STRINGP: 입력이 문자열인가?
  • FORMAT 함수: NIL을 반환. 하지만 부수효과로 디스플레이나 파일에 뭔가를 출력
    • 화면에 출력하려면 첫 번째 인자는 T, 다른 데 출력하려면 여러 가지가 있음
    • 두 번째 인자는 format control string (C의 printf의 인자와 비슷해보인다)
      • ~%는 printf의 \n
      • ~&는 새로운 줄이 아닐 때만 새로운 줄을 시작
      • ~S는 리스프 객체의 문자열 표현 (FORMAT 함수의 나머지 인자들을 채워줘야 한다)
      • ~A는 ~S에서 이스케이프 문자를 떼어내고 출력
  • READ 함수: 키보드 입력 -> 리스프 객체로 변환

(defun my-square ()
  (format t "Please type in a number: ")
  (let ((x (read)))
    (format t "The number ~S squared is ~S.~%"
      x (* x x))))
  • YES-OR-NO-P 함수: format control string을 입력으로 취해 화면에 출력한다. 사용자는 반드시 yes 또는 no로 답해야 한다.

(defun riddle ()
  (if (yes-or-no-p
        "Do you seek Zen enlightenment? ")
      (format t "Then do not ask for it!")
      (format t "You have found it.")))
  • Y-OR-N-P 함수도 있음: y 또는 n으로 답해야 한다
  • 파일 읽기
    • WITH-OPEN-FILE 매크로
      • (WITH-OPEN-FILE (var pathname) body)
      • 지역 변수를 하나 만들고 파일에 대한 연결을 나타내는 stream object로 설정한다
      • stream object는 파일 연결을 나타내기 위한 리스프의 특수 데이터타입
      • 전역 변수 *TERMINAL-IO*를 한 번 볼 것. 이게 바로 stream object의 일종
      • READ의 추가 인자로 stream object를 넘겨 파일에서 데이터를 읽을 수 있다
      • WITH-OPEN-FILE을 벗어나면 파일 연결이 자동으로 닫힌다
아 졸려.. 또 내일 =.=

9장 고급 주제

Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:30:31
Processing time 0.0989 sec