언제 필요한가요?
같은 계산을 다시 쓰거나 다른 프로시저에 전달하려면 프로시저로 만드세요. 인자의 일부나 전부가 리스트에 담겨 있다면 apply로 호출할 수 있습니다.
실행으로 확인하기
아래 프로그램 전체를 procedures.lspx로 저장하세요. 먼저 네이티브 CLI를 설치하거나, 각 예제를 플레이그라운드에서 따로 실행하세요.
(define (sum first . rest) (apply + first rest))
(sum 1 2 3 4)lispex run procedures.lspx출력
10명령의 종료 상태는 0이며 파일을 생성하지 않습니다.
예제 읽기
sum은 인자를 적어도 하나 받아야 합니다. first에는 1이 들어가고 rest에는 새 리스트 (2 3 4)가 들어갑니다. apply는 따로 적은 first와 이 리스트를 하나로 합친 뒤 +를 한 번만 호출합니다.
first와 rest처럼 값을 받을 이름을 매개변수라고 합니다. 호출할 때 넘기는 값은 인자입니다. 프로시저가 받는 인자 개수를 인자 수라고 합니다. rest 앞의 점은 남은 인자를 새 리스트에 모아 이 매개변수에 전달한다는 뜻입니다.
(apply + first rest)는 호출할 때 실행하는 코드인 프로시저 본문입니다. 이 예제에서는 (+ 1 2 3 4)와 같은 계산을 합니다.
프로시저 안에서 이름 정의하기
(define (g)
(define a 1)
(define b 2)
(+ a b))
(g)관측 결과
3a와 b는 g 안에서만 쓸 수 있는 이름입니다. 본문 맨 앞에 연속으로 쓴 define은 하나로 묶습니다. 이 묶음에서 정의한 프로시저는 서로 호출할 수 있습니다.
이 정의들은 본문 맨 위에 모아 두세요. 다른 식 뒤에 쓴 define은 맨 앞의 묶음에 들어가지 않습니다.
매개변수와 호출 형식 고르기
define은 프로시저에 이름을 붙입니다. lambda는 이름 없이 프로시저 값을 만듭니다. 둘 다 바깥 소스 구조에 따라 이름을 찾는 어휘 스코프 규칙을 따릅니다. 그 이름을 계속 사용할 수 있는 프로시저를 클로저라고 합니다.
| 형태 | 잘 맞는 용도 | 인자 수 |
|---|---|---|
(define (f x) body) | 이름 있는 재사용 프로시저 | 고정 |
(lambda (x y) body) | 익명 프로시저 값 | 고정 |
(define (f . xs) body) | 이름 있는 프로시저가 인자를 모아 받기 | 0개 이상 |
(lambda (x . rest) body) | 필수 인자와 나머지 | 고정 부분 이상 |
(apply f a final-list) | 데이터로 모은 인자 적용 | f가 검사 |
표기 (x ...)와 (x ... . rest)에서 ...는 매개변수 이름이 더 올 수 있다는 뜻입니다. 소스에 그대로 쓰는 글자가 아닙니다. lambda의 매개변수 자리는 이 두 모양을 받습니다. (lambda args ...)처럼 괄호 없이 이름만 쓰면 E130이 납니다.
나머지 인자를 받는 매개변수에는 끝이 ()인 새 올바른 리스트가 들어갑니다. apply의 마지막 인자도 올바른 리스트여야 합니다. 예를 들어 '(1 2)는 올바른 리스트입니다. '(1 . 2)는 끝이 2이므로 부적절한 리스트입니다.
본문을 실행하기 전에 인자 확인하기
리스펙스는 프로시저 식을 먼저 계산하고 인자 식을 왼쪽부터 계산합니다. 인자 수가 맞지 않으면 본문을 실행하기 전에 E302가 납니다. 예를 들어 (sum)은 sum에 필요한 인자를 하나도 전달하지 않습니다.
프로시저가 아닌 값을 호출하면 E301이 납니다. (apply + '(1 . 2))처럼 마지막 인자가 부적절한 리스트이면 E312가 납니다.
마지막 호출에 apply 쓰기
프로시저가 마지막으로 하는 일이 apply 호출이면 꼬리 위치를 유지합니다. 돌아온 뒤 할 일을 추가하지 않고, 호출한 프로시저의 결과를 그대로 반환합니다. 예제는 꼬리 호출에서 확인하세요.
리스펙스는 직접 호출과 apply를 같은 호출 처리기로 실행합니다. 리스펙스 클로저는 직접 실행하며, 구현 언어의 apply에 맡기지 않습니다.
다음으로
고차 프로시저 문서에서는 map과 filter 등에 프로시저를 전달합니다. 클로저 가이드에서는 바깥 코드가 끝난 뒤에도 프로시저가 그 이름을 사용하는 모습을 살펴봅니다.