프로시저 본문 변환은 내부 이름을 모두 먼저 만들고 대입으로 각 값을 설치한 뒤 남은 본문을 보존해 서로 재귀적인 helper가 하나의 지역 환경을 공유하게 합니다.
생각해 볼 질문
평가기는 왜 프로시저 값을 하나라도 설치하기 전에 내부 바인딩을 모두 먼저 만들어야 할까요?
프로시저 본문 앞쪽의 내부 정의 알아보기
프로시저 정의 축약형을 명시적인 lambda 값으로 바꾸기
대입 전에 모든 지역 이름을 unassigned 표식으로 만들기
대입 뒤에 정의가 아닌 원래 본문 순서 보존하기
원래 프로그램과 변환한 프로그램을 같은 유한 입력으로 비교하기
화면에 나온 변환과 입력의 일치 관찰하기
scan-out 단계는 프로시저 본문을 데이터로 읽습니다. 앞쪽 define 형식을 모으고 프로시저 정의 축약형을 lambda 식으로 바꾸며, 각 이름을 명시적인 unassigned 표식으로 갖는 let 바인딩을 먼저 만듭니다. 그다음 각 정의의 set!을 놓고 마지막에 원래 남은 본문을 붙입니다. 따라서 어느 클로저가 적용되기 전에 이름들이 한 환경 안에 함께 존재해 서로 재귀적인 helper가 같은 지역 환경을 공유합니다.
두 번째 프로그램은 두 형태를 실제로 실행합니다. classify-original은 내부 정의를 그대로 사용하고 classify-scanned는 변환된 let과 set! 구조를 명시적으로 씁니다. 7과 8에서 같은 결과가 나오며 equal?은 참을 보고합니다. 이 유한 비교는 화면에 나온 변환과 입력의 일치를 확인하고 중간 구조를 직접 드러냅니다.
변환 프로그램은 even-step과 odd-step을 인용된 *unassigned* 표식에 바인딩한 let 식 하나와 두 set! 형식, 원래 list 본문을 반환합니다. 비교 프로그램은 (#t ((#f #t) (#t #f)) ((#f #t) (#t #f)))를 반환합니다.
실행 추적에서 볼 점
변환 실행에서는 인용된 소스 데이터를 읽는 일과 앞쪽 정의 모으기, 바인딩 만들기, 프로시저 정의를 lambda 값으로 바꾸기, 대입 만들기, 남은 본문 붙이기를 구분하세요. 비교 실행에서는 하나의 let 환경과 두 set!, 번갈아 호출되는 클로저를 찾으세요. 일치는 선택한 입력에 대해서만 보고됩니다.
직접 해보기
프로그램을 수정하고 결과를 비교해 보세요.
세 개의 서로 재귀적인 나머지 프로시저로 3의 배수를 알아보는 내부 helper를 추가하세요. 원래 버전과 명시적으로 scan-out한 버전을 모두 쓰고 8과 9의 결과를 비교하세요.
힌트 보기
대입 전에 이름 세 개를 모두 만드세요. 각 클로저는 1을 빼고 다음 나머지 프로시저를 호출하며 나머지 0 프로시저만 0에서 참을 반환합니다.