이름을 해석하고 경로를 고르고 경계를 지키세요.
실행 프로그램 열일곱 개로 제1장의 수업 열여덟 개를 다시 연결합니다. 먼저 값과 경로를 예상하고 중요한 바인딩이나 추상화 경계를 찾은 뒤 화면에 보인 프로세스가 마지막 결과에 무엇을 더하는지 설명하세요.
어느 환경이 이름을 공급하는지, 어느 술어가 작업을 선택하는지, 프로시저가 무엇을 비공개로 두는지, lambda가 동작을 어떻게 만드는지, 소수 검사가 무엇을 확정하는지, 프로세스가 어떻게 자라는지 설명할 수 있나요?
- 연산자와 피연산자를 하나의 조합식으로 읽기
- 정의와 매개변수와 let 바인딩이 이름을 해석하는 과정 설명하기
- 적용적 순서와 정상 순서 치환 경로를 데이터로 구성하기
- cond와 if와 단락 술어의 경로 따라가기
- 공개 프로시저 계약과 지역 도우미 바인딩 구분하기
- 프로시저 호출 환경 설명하기
- 재귀와 반복 프로세스 비교하기
- 프로시저 값을 인자로 전달하기
- 어휘 범위를 가진 익명 프로시저 만들고 적용하기
- 프로시저를 반환하고 합성해 새 변환 만들기
- 문제를 줄이며 불변식 유지하기
- 수치 추측값 반복 개선하기
- 유한 연분수 누적하기
- 고정점 변환 되풀이하기
- 빠르게 지수 줄이기
- 모듈러 축약과 고정 밑 페르마 결론의 범위 제한하기
- 시험 나눗셈의 근거와 probable-prime 근거 구분하기
- 트리 재귀 호출 성장과 반복 단계 비교하기
먼저 이름과 치환을 살펴보세요. 환경 예제는 전역·매개변수·let 지역 바인딩을 나눕니다. 치환 데이터는 피연산자를 먼저 줄이는 경로와 식을 먼저 넣는 경로를 비교하지만 리스펙스의 숨은 프레임을 보여 주는 것은 아닙니다.
다음에는 제어를 따라가세요. cond는 처음 일치한 절을 고르고 if는 0으로 나누는 연산을 보호하며 단락 형식은 도달할 필요가 없는 record! 호출을 남기지 않습니다. #f만 거짓이므로 인용한 기호도 참 갈래를 선택할 수 있습니다.
블랙박스 예제는 choose와 square를 지역에 두면서 공개 프로시저 계약 하나를 유지합니다. 내부 분해가 달라도 호출자는 입력과 결과에만 의존할 수 있습니다. 유한한 표본의 일치는 화면에 나온 입력에서 두 분해가 같은 결과를 낸다는 사실을 확정합니다.
팩토리얼 두 프로세스를 비교한 뒤 프로시저 값을 따라가세요. sum은 term과 next를 받고, 중첩 lambda는 이름을 어휘적으로 찾으며, compose는 나중 적용에서만 포획한 동작을 실행하는 프로시저를 만듭니다.
유클리드는 순서쌍을 줄이면서 최대공약수를 보존합니다. 제곱근 개선과 연분수와 고정점은 서로 다른 완전한 다음 상태를 넘깁니다. 빠른 거듭제곱과 expmod는 남은 문제를 줄이면서 명시적인 수치 관계를 보존합니다.
마지막으로 소수 검사 절차를 비교하세요. 시험 나눗셈은 제곱근 경계를 소진하여 화면의 작은 후보를 판정하지만 선택한 페르마 밑은 카마이클 수 561도 받아들입니다. 그 뒤 되풀이되는 피보나치 호출 트리와 고정 크기 반복 상태를 대조하세요.
- 출력
- —
- 값
- —
- 진단
- —
환경 프로그램은 ((global-x 10) (parameter-x 3 local-y 4 sum 7) (global-after 10))을 반환합니다. 치환 프로그램은 25로 끝나는 적용적·정상 축약 목록을 반환합니다. 조건식 프로그램은 (negative zero small-positive large-positive undefined 5 truthy)를 반환하고 블랙박스 프로그램은 (13 61 global-marker)를 반환합니다. 팩토리얼 두 프로그램은 40320입니다. 고차 합은 55이고 중첩 lambda는 (100 7 100), 반환되는 프로시저 합성은 (49 37 15)를 반환합니다. 소수 비교는 ((prime-7 #t #t) (composite-15 #f #f) (carmichael-561 #f #t))를 반환하며 피보나치 계측은 (21 67 8)을 반환합니다.
가장 가까운 환경 바인딩을 찾고 인용한 치환 데이터와 실제 적용을 구분하세요. 선택된 각 술어 갈래와 생략된 호출을 확인하고 지역 블랙박스 도우미의 진입과 종료를 전역 이름과 구분하세요. lambda 생성과 적용, 프로시저 인자와 반환값, 유클리드, 수치 개선, 지수와 모듈러 축약, 시험 약수, 선택한 페르마 밑, 되풀이되는 피보나치 호출을 차례로 따라가세요. 실행 흐름은 선택한 실행을 고정 런타임 한도 아래에서 정확히 기록합니다.
환경과 제어와 추상화를 생각하는 질문 열일곱 개
프로시저 정의는 표현식을 어떻게 재사용할 수 있는 방법으로 만들까요?
정답 프로시저를 적용하면 새 환경에서 각 매개변수가 인수 값에 묶입니다. 그 환경에서 본문을 평가하므로 3과 4를 넣은 sum-of-squares는 25를 반환합니다.
재귀적 프로세스와 반복적 프로세스는 무엇이 다를까요?
정답 두 정의 모두 자기 자신을 호출하므로 재귀 프로시저입니다. 하지만 두 번째 정의만 고정된 개수의 상태 변수로 요약할 수 있는 반복적 프로세스를 만듭니다.
하나의 프로시저로 여러 종류의 합을 어떻게 나타낼 수 있을까요?
정답 이 분리는 중요한 설계 습관의 시작입니다. 바뀌지 않는 프로세스에는 한 번 이름을 붙이고 달라지는 결정은 인수로 넘깁니다.
a와 b를 b와 나머지로 바꿔도 최대공약수가 유지되는 이유는 무엇일까요?
정답 각 호출은 (a, b)를 (b, remainder(a, b))로 바꿉니다. 두 번째 값이 0이 되면 첫 번째 값이 보존된 최대공약수입니다. 재귀 호출 뒤로 미뤄 둔 계산은 없습니다.
한 번의 개선 규칙이 어떻게 점점 더 정확한 수치 프로세스를 만들까요?
정답 이 프로그램들은 숨은 오차 한도 대신 개선 횟수를 고정합니다. 1.0에서 여섯 번 개선하면 √2에 대한 결정적인 관찰을 얻으며 명시적인 이력에서는 추측값 사이의 차이가 빠르게 줄어드는 모습을 볼 수 있습니다.
평가 순서는 유한 연분수 값을 바꾸지 않으면서 프로세스의 모양을 어떻게 바꿀까요?
정답 반복 버전은 k번 항에서 시작하며 0.0을 이미 계산한 꼬리로 둡니다. 호출마다 result를 완성된 연분수 층 하나로 바꾸고 1번 항을 향해 갑니다. 두 예제는 분자와 분모 열 개가 모두 1.0이므로 황금비의 역수에 대한 같은 유한 근삿값을 반환합니다.
변환을 적용해도 바뀌지 않는 값을 찾는다는 말은 무엇을 뜻할까요?
정답 fixed-point는 관찰할 수 없는 오차 한도 안에 정지 결정을 숨기지 않습니다. 변환과 현재 추측값과 남은 횟수가 프로세스의 완전한 상태입니다. 이력을 만드는 버전은 같은 값 전달을 기록해 번갈아 나타나는 추측값을 그대로 보여 줍니다.
계산하는 거듭제곱을 바꾸지 않으면서 지수를 어떻게 빠르게 줄일까요?
정답 exponent가 0이 되면 남은 거듭제곱은 1이고 product에 답이 모두 들어 있으므로 프로세스가 멈춥니다. 이력 프로그램은 모든 호출의 전체 상태를 기록해 짝수 전이와 홀수 전이를 직접 확인하게 합니다.
모듈러 합동 하나로 후보 수에 관해 무엇을 알 수 있고 무엇은 알 수 없을까요?
정답 passes-base-2?는 2의 candidate승과 2가 candidate를 모듈러스로 한 합동인지 점검합니다. 17은 통과하고 15는 실패합니다. 3과 11과 17을 곱한 합성수 561도 통과합니다. 각 결과는 밑 2의 합동을 정확히 기록하며 시험 나눗셈은 합성수 분류를 제공합니다.
같은 피보나치 값을 돌려주는 두 프로시저의 시간과 공간 요구량은 어떻게 다르게 자랄까요?
정답 n이 8이면 계측한 트리는 67번의 프로시저 적용 뒤 21을 반환하고 깊이 8에 닿습니다. 반복 프로세스는 이웃한 피보나치 값 두 개와 남은 횟수만 가지고 같은 값에 여덟 단계 만에 닿습니다. 이 교과서적인 두 프로시저에서 단순 재귀의 시간은 지수적으로 자라고 깊이는 선형으로 자라며 반복 프로세스의 시간은 선형이고 상태 변수 수는 일정합니다. 화면의 숫자는 여전히 이 유한한 프로그램과 입력만 설명합니다.
프로시저 호출의 결과가 또 다른 프로시저라면 어떤 새로운 구성이 가능해질까요?
정답 average-damp도 프로시저를 반환합니다. 반환된 변환은 원래 f를 x에 적용한 값과 x의 평균을 계산합니다. 제곱근 변환 y ↦ 16/y에 추측값 2를 넣으면 5가 되고 고정점인 4를 넣으면 그대로 4가 됩니다. 프로시저 생성자는 재사용할 방법을 가지고 있고, 전달된 f가 실제로 바꿀 동작을 결정합니다.
전역 정의와 매개변수와 지역 이름이 함께 있을 때 평가기는 한 이름이 어떤 값을 뜻하는지 어떻게 결정할까요?
정답 describe를 호출하면 바깥 환경에 x = 10이 있어도 매개변수 x = 3을 가진 새 환경이 생깁니다. 안쪽 let은 그 호출 환경에 y를 더합니다. 본문의 조회는 가장 가까운 같은 이름의 바인딩을 먼저 찾으므로 호출 안에서는 x가 3이고 호출 전후 바깥에서는 10입니다. 지역 y는 호출이 끝나면 더 이상 보이지 않습니다.
치환은 프로시저 적용에서 무엇을 드러내며 인자를 본문에 넣기 전이나 후에 평가하면 무엇이 달라질까요?
정답 두 번째 프로그램은 호출 횟수를 늘리는 argument 프로시저로 작업 차이를 보이게 합니다. 보통의 리스펙스 호출 (square (argument))은 square를 적용하기 전에 인자를 한 번 평가합니다. 명시적으로 쓴 normal-model 일정은 본문에서 x를 두 번 쓰므로 argument를 두 번 호출합니다. 두 일정은 모두 25를 반환하지만 호출 수가 다릅니다. 이 비교는 코드로 명시한 두 유한 일정과 정확한 호출 횟수를 기록합니다.
프로그램은 가능한 모든 갈래의 작업을 먼저 실행하지 않고 어떻게 하나의 결정을 내릴까요?
정답 and와 or는 평범한 프로시저가 아니라 제어 형식입니다. and는 첫 거짓 값에서 멈추고 or는 첫 #f가 아닌 값에서 멈춥니다. 두 번째 프로그램은 실제로 닿은 피연산자만 기록하여 실행하지 않은 식을 기록의 부재로 드러냅니다. 이 유한 실행은 선택한 경로와 단락 평가 순서를 정확히 기록합니다.
프로시저 안의 무엇이 바뀌어도 모든 호출자를 함께 고칠 필요가 없을까요?
정답 두 번째 프로그램은 같은 표본 결과를 서로 다른 분해로 계산합니다. 가장 큰 두 수를 직접 고르거나 세 제곱의 합에서 가장 작은 수의 제곱을 뺍니다. 내부 작업이 달라도 공개 질문은 같습니다. 한 유한 입력의 일치는 화면에 나온 표본에서 두 분해가 같은 결과를 낸다는 사실을 확정합니다.
각 소수 검사 프로시저는 무엇을 확정하며, true를 반환한 뒤에도 무엇이 결정되지 않은 채 남을까요?
정답 페르마 프로그램은 선택한 밑마다 a의 n제곱이 n으로 나눈 나머지에서 a와 합동인지 검사합니다. 소수 7은 통과하고 합성수 15는 빠르게 실패하며 합성수 561은 시험 나눗셈으로 인수를 찾을 수 있는데도 서로소인 세 밑을 통과합니다. 화면의 결과는 선택한 페르마 절차에 따른 probable-prime 판정을 기록합니다.
lambda 식은 어떤 값을 만들며, 그 값을 나중에 적용할 때 본문은 어느 바인딩을 사용할까요?
정답 중첩 예제에서 바깥 lambda는 x를 3에 묶고 안쪽 lambda는 y를 4에 묶습니다. 안쪽 본문은 현재 호출에서 y를, 둘러싼 어휘 환경에서 x를 찾습니다. 전역 x는 100으로 남습니다. 마지막 식은 새로 만든 square 프로시저를 다른 lambda에 전달하여 프로시저 생성과 바인딩과 전달과 적용이 모두 보통 값 모형을 따른다는 점을 보여 줍니다.