생각해 볼 질문
식이 프로시저를 만들 때와 그 프로시저를 나중에 적용할 때 각각 어느 환경을 사용할까요? 환경을 순서가 있는 프레임 리스트로 표현하기 이름 조회에서 가장 가까운 프레임부터 찾기 복합 프로시저 안에 생성 환경 포획하기 적용할 때 포획한 환경에 새 매개변수 프레임 연결하기 첫 프레임의 정의와 기존 바인딩의 대입 구분하기 model-eval은 자기 평가 값과 변수와 quote와 lambda와 적용을 처리합니다. lambda를 평가하면 매개변수와 본문과 현재 환경을 담은 compound-procedure 데이터를 만듭니다. 그 값을 적용하면 포획한 환경에 새 매개변수 프레임을 연결하여 본문을 평가하므로 안쪽 y 호출은 바깥 x를 계속 찾을 수 있습니다.
두 번째 프로그램은 프레임 변경을 직접 드러냅니다. 조회는 전역 x보다 지역 x를 먼저 찾습니다. set-variable-value!는 이미 있는 지역 레코드를 바꾸고 define-variable!은 첫 프레임에 y를 더합니다. 어느 연산도 전역 x를 바꾸지 않습니다. 이 설명용 평가기는 나열한 형식의 환경 규칙을 직접 모델링합니다.
SICP 코드 UTF-8 2,593 / 1,048,576바이트
( begin
( define ( make-frame variables values )
( if ( null? variables )
' ( )
( cons ( cons ( car variables ) ( car values ) )
( make-frame ( cdr variables ) ( cdr values ) ) ) ) )
( define ( extend-environment variables values base )
( cons ( make-frame variables values ) base ) )
( define ( lookup-variable-value variable environment )
( if ( null? environment )
' unbound-variable
( let ( ( record ( assoc variable ( car environment ) ) ) )
( if record
( cdr record )
( lookup-variable-value variable ( cdr environment ) ) ) ) ) )
( define ( sum-values values )
( if ( null? values )
0
( + ( car values ) ( sum-values ( cdr values ) ) ) ) )
( define ( product-values values )
( if ( null? values )
1
( * ( car values ) ( product-values ( cdr values ) ) ) ) )
( define ( apply-primitive name arguments )
( cond ( ( eq? name ' primitive-plus ) ( sum-values arguments ) )
( ( eq? name ' primitive-times ) ( product-values arguments ) )
( else ' unknown-primitive ) ) )
( define ( eval-operands expressions environment )
( if ( null? expressions )
' ( )
( cons ( model-eval ( car expressions ) environment )
( eval-operands ( cdr expressions ) environment ) ) ) )
( define ( model-apply procedure arguments )
( if ( and ( pair? procedure )
( eq? ( car procedure ) ' compound ) )
( model-eval
( caddr procedure )
( extend-environment
( cadr procedure )
arguments
( cadddr procedure ) ) )
( apply-primitive procedure arguments ) ) )
( define ( model-eval expression environment )
( cond ( ( or ( number? expression )
( string? expression )
( boolean? expression ) )
expression )
( ( symbol? expression )
( lookup-variable-value expression environment ) )
( ( eq? ( car expression ) ' quote )
( cadr expression ) )
( ( eq? ( car expression ) ' lambda )
( list ' compound
( cadr expression )
( caddr expression )
environment ) )
( else
( model-apply
( model-eval ( car expression ) environment )
( eval-operands ( cdr expression ) environment ) ) ) ) )
( define global-environment
( list ( list ( cons ' + ' primitive-plus )
( cons ' * ' primitive-times ) ) ) )
( list
( model-eval
' ( ( lambda ( x )
( ( lambda ( y ) ( + x y ) ) 4 ) )
3 )
global-environment )
( model-eval
' ( ( ( lambda ( x )
( lambda ( y ) ( + x y ) ) )
5 )
8 )
global-environment ) ) ) 코드 실행Ctrl/⌘ Enter 파일 열기 코드 저장 코드 복사 편집기 지우기
예제 명시적인 환경 모형에서 클로저 만들고 적용하기 조회와 정의와 대입 비교하기
실행은 브라우저 안에서 이루어지며 프로그램 결과와 실행 추적을 보여줍니다. 예상 결과 수업용 평가기는 (7 13)을 반환합니다. 프레임 변경 프로그램은 (3 4 7 10)을 반환합니다.
실행 추적에서 볼 점 평가기에서는 lambda 평가와 프로시저 적용을 나누고 각 compound 값이 포획한 부모 환경을 따라가세요. 프레임 프로그램에서는 가장 가까운 assoc 성공, 지역 x를 바꾸는 set-cdr!, 첫 프레임에 y를 더하는 set-car!를 찾으세요.
직접 해보기 프로그램을 수정하고 결과를 비교해 보세요. model-eval에 if를 추가하고 지역 매개변수와 포획한 바깥 값 중 하나를 고르는 클로저를 평가하세요. 전역 y도 정의한 뒤 지역 정의가 전역을 바꾸지 않고 가리는지 확인하세요.
힌트 보기 선택한 if 갈래만 평가하세요. define은 주어진 환경의 첫 프레임을 대상으로 합니다.