1. 프로시저를 써서 요약하는 방법

1. LISP를 사용하는 이유

- 프로그램을 짜는데 필요한 여러 원소와 갖가지 데이터 구조를 공부하고, 이를 뒷받침하는 데 어떤 언어 기능이 있어야 하는지를 관계지어 설명하기에 알맞은 점이 많다


1.1 프로그램을 짤때 바탕이 되는 것

- 프로그래밍 언어는 프로세스에 대한 사람의 생각을 짜임새 있게 담아내는 그릇

- 프로그래밍의 구성 요소

  - 프로시저 : 데이터를 처리하는 규칙을 적은 것

  - 데이터 : 프로시저에서 쓰려는 물건(stuff)

- 좋은 프로그래밍 언어의 세가지 표현 방식

  - 기본식

  - 엮어내는 수단(means of combination) : 간단한것을 모아 복잡한것으로 만든다

  - 요약하는 수단(means of abstraction) : 복잡한 것에 이름을 붙여 하나로 다룰 수 있게끔 간추린다

  --> 기본 데이터와 기본 프로시저를 나타내고, 프로시저들과 데이터들을 엮어서 더 복잡한 것을 만들고 이를 간단하게 요약하는 수단이 반드시 있어야 함


 1.1.1 식(expression)

- 셈(evaluation)

- 엮은식(combination) : 여러식을 괄호로 묶어 리스트를 만들고 프로시저 적용(procedure application)을 하도록 엮어 놓은 식

- 연산자(operator), 피연산자(operand), 인자(argument)

- 앞가지 쓰기(prefix notation) : 연산자를 피연산자 왼쪽에 두는 방식

  - 장점 1 : 인자가 많아져도 따로 문법을 만들 필요 없이 쓰던 그대로 쓸수 있다

  - 장점 2 : 식속에 다시 식을 넣어서 식을 여러 겹으로 엮어 늘리기가 쉽다

- 가지런히 쓰기(pretty-printing) : 식을 깊이 겹쳐 써야 할 때 인자를 중심으로 줄을 맞추고 알맞게 들여 쓰는 방식

- read-eval-print loop


1.1.2 이름과 환경

- 변수 : 계산하는 물체(computational object)에 붙이는 이름

- define : 이름을 붙이는 방법(scheme)

  - 합친연산(compound operation)을 간추리는 수단 

- 실행기(interpreter)구성

  - 메모리 : 이름-물체의 쌍을 저장 --> 환경


1.1.3 엮은식(combination)을 계산하는 방법

- 엮은식의 값을 계산하는 차례

  1. 엮은식에서 부분 식(subexpression)의 값을 모두 구한다

  2. 엮은식에서 맨 왼쪽에 있는 식(연산자)의 값은 프로시저가 되고, 나머지 식(피연산자)의 값은 인자가 된다. 프로시저를 인자에 적용하여 엮은식의 값을 구한다

  --> 되도는(recursive)프로세스

- 나무꼴 어큐뮬레이션(tree accumulation)

  - node : 연산자

  - branch : 연산할 것들


1.1.4 묶음 프로시저(compound procedure)

- 프로시저 정의 : 복잡한 연산에 이름을 붙여서 쓰는 방법

  - (define (<name> <formal paramegters>) <body>)

  - <name> : 환경에서 프로시저를 가리키는 이름

  - <formal parameters> : 프로시저가 받아오는 인자를 가리키기 위해서 프로시저의 몸속에서 쓰는 이름

  - <body> : 프로시저를 불러 쓸 때마다 계산할 식


1.1.5 맞바꿈 계산법(substitution model)으로 프로시저를 실행하는 방법

- 묶음 프로시저를 인자에 맞춘다는것은 프로시저의 몸 속에 있는 모든 인자 이름(formal parameter)을 저마다 그에 대응하는 인자 값으로 맞바꾼 다음, 그렇게 얻어낸 식의 값을 구하는 것이다

- 샘플 (f 5)

(define (square x) (* x x))


(define (sum-of-squares x y)

 (+ (square x) (square y)))


(define (f a)

 (sum-of-squares (+ a 1) (* a 2)))

1. a -> 5로 맞바꿈 : (sum-of-square (+ 5 1) (* 5 2)) --> 연산자 : sum-of-square, 피연산자 : (+ 5 1), (* 5 2)

2. sum-of-squares x y -> x=6, y=10으로 맞바꿈 : (+ (square 6) (square 10)) --> 연산자 : + , 피연산자 : (square 6), (square 10)

3. square x -> x = 6 , x= 10 두개 식 맞바꿈 : (+ (* 6 6) (* 10 10))

4. (+ 36 100)

5. 136

- 실제 실행기가 돌아가는 방법은 아니며, 식의 계산 과정(evaluation process)에 대하여 제대로 형식을 갖추고 생각을 정리하려는 출발점일 뿐

- 실제로는 갇힌환경(local environment)에 인자 이름을 넣어놓고 계산하는 방법을 사용함

- 인자먼저 계산법(applicative order) : LISP 실행기 적용 방식

  - 인자값 먼저 구하는 방법

  - 같은식을 여러번 되풀이 계산하는 경우가 생기지 않아 좀 더 빠름, 정의 대로 계산하는 규칙이 더 복잡함


- 정의대로 계산법(normal order)

  - 인자값을 계산하지 않고 식 자체를 인자이름으로 맞바꾸어 가다가 마지막에 기본연산으로만 이루어진 식을 얻을때 값을 구하는 방법

  - 끝까지 펼친 다음에 줄이는 계산 방법

  (f 5)

  1. (sum-of-square (+ 5 1) (* 5 2))

  2. (+ (square (+ 5 1) (square (* 5 2))))

  3. (+ (* (+5 1) (+5 1)) (* (* 5 2) (*5 2))) : (+ 5 1), (* 5 2)를 두번 계산함

  4. (+ (* 6 6) (* 10 10))

  5. (+ 36 100)

  6. 136


1.1.6 조건 식과 술어(predicate)

- 조건식 문법(cond)

  (cond (<p1> <e1>)

        (<p2> <e2>)

        ...

        (<pn> <en>)

        (else <e>)

  - 절(clause) : 두식을 괄호로 묶어 놓음 (<p1> <e1>)

  - 술어(predicate) : 참/거짓

- 조건식 문법(if)

  (if <predicate> <consequent> <alternative>)

- 논리연산(and, or, not)

  (and <e1> ... <en>) : 왼쪽에서 오른쪽으로 계산, 그 가운데 거짓이라 대답하는 <e>가 나오면 and 값은 거짓이 되고 나머지<e>는 구하지 않음. 모든<e>가 참일 때에는 마지막 식의 값을 반환

  (or <e1> ... <en>) : 왼쪽에서 오른쪽으로 계산, 그 가운데 참이라 답하는 <e>가 나오면 참이되고, 나머지는 구하지 않음. 모든<e>가 거짓일 때에는 마지막 식의 값을 반환

  (not <e>) : <e>가 참이면 거짓, 거짓이면 참


1.1.7  연습 : 뉴튼 법으로 제곱근 찾기

- 함수와 프로시저 차이

  - 프로시저는 효율성을 갖춰야 함

  - 함수 : 무엇이 어떤 성질을 지니는지 밝힘

  - 프로시저 : 무엇을 어떻게 만들지/구할지 나타내는 일

- 뉴튼법 : 얻고자 하는 값에 가까운 값을 차례로 되풀이해서 구해 나감


1.1.8 블랙박스처럼 간추린 프로시저

- 프로시저 요약하기 : 프로시저를 묶어서 간추려 놓은 이름

- Block Structure : 프로시저 정의를 겹쳐쓰는 모양

- lexical scoping 규칙 : 문범에 따라 변수가 보이는 넓이가 정해지는 규칙



+ Recent posts