값, 이름, 프로시저

리스펙스가 리터럴과 호출을 읽는 방법을 익히고, definelet으로 이름을 붙이고, lambda로 재사용할 프로시저를 만듭니다.

1단계에서는 완전한 프로그램을 실행했습니다. 이번에는 그 안에 들어 있던 몇 가지 모양을 차근차근 떼어 봅니다. 값이 하나 있고, 그 값을 가리키는 이름이 따로 있고, 값을 계산해 내는 프로시저 호출이 또 하나 있습니다. 이 셋을 구분하는 일이 이번 공부의 대부분이고, 셋만 잘 나누어 두면 괄호는 일정한 순서로 읽힙니다.

값은 홀로 설 수 있습니다

수, 불리언, 문자열, 문자, 인용 데이터는 값입니다.

LISPEX
42
#t
"Lispex"
#\L
'ready

글자 하나를 값으로 적을 때는 #\ 뒤에 그 글자를 씁니다. 위의 #\L이 대문자 L 하나입니다. 큰따옴표로 감싼 문자열과는 다른 종류입니다.

작은따옴표를 붙이지 않은 ready는 다르게 동작합니다. 따옴표 없이 적으면 리스펙스는 ready를 이름으로 보고 그 값을 찾습니다. 앞에 따옴표를 붙여 'ready라고 쓰면 심볼이 데이터로 그대로 남습니다.

호출 위치의 리스트는 일을 요청합니다

가장 안쪽 호출부터 시작해 바깥으로 하나씩 짚어 나가면 아래 식도 읽어 낼 수 있습니다.

LISPEX
(+ 10 (* 2 3))

리스펙스는 가장 먼저 +를 찾고, 이어서 10을 평가한 다음, (* 2 3)을 왼쪽부터 차례로 평가합니다. 안쪽 호출의 결과가 6이므로 바깥 호출은 (+ 10 6)을 계산하는 것과 같습니다.

결과

OUTPUT
16

괄호로 둘러싸인 형태가 모두 평범한 호출인 것은 아닙니다. define, lambda, let, if는 식의 어느 부분을 평가할지, 이름이 어디에서 보일지를 정합니다.

값에 이름 붙이기

define은 최상위 바인딩을 새로 만들거나 기존 바인딩을 갱신합니다. 여기서 바인딩은 파일의 나머지 부분에서 그대로 쓸 수 있는 이름을 뜻합니다.

LISPEX
(define fee 3)
(+ 20 fee)

결과

OUTPUT
23

정의하는 문장 자체는 아무것도 출력하지 않습니다. 마지막 덧셈만 값 하나를 출력합니다.

이름이 본문 한 곳에서만 필요하다면 let을 쓰세요.

LISPEX
(let ((base 7))
  (* base 2))

결과

OUTPUT
14

여기서 만든 base 바인딩은 본문 안에서만 보이고 본문이 끝나면 사라집니다. 범위를 이렇게 좁게 잡아 두면 임시 계산을 살펴보기가 한결 쉬워집니다.

다시 쓰는 프로시저 만들기

프로시저를 정의하는 축약 형식을 쓰면 프로시저 이름과 입력 이름을 한자리에 나란히 적을 수 있습니다.

LISPEX
(define (price-with-fee price fee)
  (+ price fee))

(price-with-fee 20 3)

결과

OUTPUT
23

같은 내용을 긴 형식으로 풀어 쓰면 프로시저도 하나의 값이라는 사실이 훨씬 잘 드러납니다.

LISPEX
(define price-with-fee
  (lambda (price fee)
    (+ price fee)))

프로시저는 전달받은 인자의 개수를 검사합니다. price-with-fee에 인자를 하나만 주거나 세 개를 주면 기본값을 임의로 채우지 않고 런타임 오류를 냅니다.

조각을 합치기

LISPEX
(define (price-with-fee price fee)
  (+ price fee))

(list (price-with-fee 20 3)
      (let ((base 7))
        (* base 2)))

결과

OUTPUT
(23 14)

여기서는 순서를 눈여겨보세요. 리스트의 첫 번째 원소를 계산한 다음 두 번째 원소를 계산합니다. list는 그렇게 준비된 두 값을 받아 (23 14)를 만듭니다.

연습

입력에 3을 곱하는 triple이라는 프로시저를 정의해 보세요. 그런 다음 list 안에서 두 번 호출해 (12 21)을 반환하세요.

답 하나 보기
LISPEX
(define (triple n)
  (* n 3))

(list (triple 4) (triple 7))

3단계로 갈 준비가 됐나요?

작은따옴표로 인용한 심볼이 왜 데이터로 남는지, (* 2 3)이 왜 바깥 덧셈보다 먼저 끝나는지, let으로 만든 이름이 어디에서 보이는지 설명할 수 있으면 다음으로 넘어가도 좋습니다. 다음은 코드가 곧 데이터입니다.