생각해 볼 질문
순서쌍이 호스트 pair가 아니라 정수 주소로 표현되어도 car와 cdr와 변경과 공유를 어떻게 유지할까요? 순서쌍 포인터와 원자 데이터를 구분하기 나란한 car·cdr 벡터의 같은 슬롯 할당하기 포인터 인덱스로 car, cdr, set-car!, set-cdr! 구현하기 유한한 목록을 벡터 메모리에 만들고 다시 읽기 서로 다른 두 셀이 표현된 꼬리 하나를 공유하는 모습 관찰하기 무한 메모리를 가정하지 않고 용량 소진을 드러내기 메모리 객체는 길이가 같은 벡터 두 개와 free 인덱스 하나를 가집니다. 표현된 순서쌍은 (ptr index)이고 memory-car와 memory-cdr는 두 벡터의 같은 인덱스를 읽습니다. list->memory는 꼬리를 먼저 만들므로 (a b c)의 루트는 세 셀 중 마지막 주소가 됩니다.
공유 예제는 꼬리 하나를 할당한 뒤 서로 다른 바깥 셀 두 개의 cdr에 같은 포인터를 저장합니다. 그 포인터의 car 슬롯 하나를 바꾸면 두 목록의 관찰이 함께 바뀝니다. 고정 벡터 용량과 계속 증가하는 free 포인터는 수집기가 살아 있는 셀을 다시 배치하기 전까지 명시적인 한계로 남습니다.
SICP 코드 UTF-8 2,879 / 1,048,576바이트
( begin
( define ( tagged-list? value tag )
( and ( pair? value ) ( eq? ( car value ) tag ) ) )
( define ( make-vector-memory capacity )
( list ' *vector-memory*
( make-vector capacity ' *unused* )
( make-vector capacity ' *unused* )
( cons ' free 0 ) ) )
( define ( memory-cars memory ) ( list-ref memory 1 ) )
( define ( memory-cdrs memory ) ( list-ref memory 2 ) )
( define ( memory-free-cell memory ) ( list-ref memory 3 ) )
( define ( memory-free memory ) ( cdr ( memory-free-cell memory ) ) )
( define ( set-memory-free! memory value )
( set-cdr! ( memory-free-cell memory ) value ) )
( define ( pair-pointer? value ) ( tagged-list? value ' ptr ) )
( define ( make-pointer index ) ( list ' ptr index ) )
( define ( pointer-index pointer ) ( cadr pointer ) )
( define ( allocate-pair! memory car-value cdr-value )
( let ( ( index ( memory-free memory ) ) )
( if ( >= index ( vector-length ( memory-cars memory ) ) )
( error "vector memory exhausted" index )
( begin
( vector-set! ( memory-cars memory ) index car-value )
( vector-set! ( memory-cdrs memory ) index cdr-value )
( set-memory-free! memory ( + index 1 ) )
( make-pointer index ) ) ) ) )
( define ( memory-car memory pointer )
( vector-ref ( memory-cars memory ) ( pointer-index pointer ) ) )
( define ( memory-cdr memory pointer )
( vector-ref ( memory-cdrs memory ) ( pointer-index pointer ) ) )
( define ( set-memory-car! memory pointer value )
( vector-set! ( memory-cars memory )
( pointer-index pointer )
value ) )
( define ( set-memory-cdr! memory pointer value )
( vector-set! ( memory-cdrs memory )
( pointer-index pointer )
value ) )
( define ( list->memory values memory )
( if ( null? values )
' ( )
( let ( ( tail ( list->memory ( cdr values ) memory ) ) )
( allocate-pair! memory ( car values ) tail ) ) ) )
( define ( memory->list pointer memory )
( if ( null? pointer )
' ( )
( cons ( memory-car memory pointer )
( memory->list ( memory-cdr memory pointer )
memory ) ) ) )
( define ( used-vector-prefix values count )
( define ( loop index result )
( if ( = index count )
( reverse result )
( loop ( + index 1 )
( cons ( vector-ref values index ) result ) ) ) )
( loop 0 ' ( ) ) )
( define memory ( make-vector-memory 8 ) )
( define root ( list->memory ' ( a b c ) memory ) )
( list ( memory-free memory )
root
( memory->list root memory )
( memory-car memory root )
( memory-car memory ( memory-cdr memory root ) )
( memory-car memory
( memory-cdr memory
( memory-cdr memory root ) ) )
( used-vector-prefix
( memory-cars memory )
( memory-free memory ) )
( used-vector-prefix
( memory-cdrs memory )
( memory-free memory ) ) )
) 코드 실행Ctrl/⌘ Enter 파일 열기 코드 저장 코드 복사 편집기 지우기
예제 세 셀 목록을 할당하고 다시 읽기 표현된 꼬리 하나를 공유하고 변경하기
실행은 브라우저 안에서 이루어지며 프로그램 결과와 실행 추적을 보여줍니다. 예상 결과 목록 할당은 (3 (ptr 2) (a b c) a b c (c b a) (() (ptr 0) (ptr 1)))을 반환합니다. 공유 실행은 (3 (left changed) (right changed) #t 0)을 반환합니다.
실행 추적에서 볼 점 꼬리를 먼저 만들기 때문에 c, b, a가 인덱스 0, 1, 2에 놓이는지 free 포인터를 따라가세요. 공유 실행에서는 바깥 cdr 슬롯 두 개가 같은 (ptr 0)을 담고 car 벡터 슬롯 하나만 바뀌는지 확인하세요.
직접 해보기 프로그램을 수정하고 결과를 비교해 보세요. 마지막 표현 cdr 슬롯만 바꾸는 파괴적 append를 구현하세요. 이어 순환을 만들고 다시 방문한 포인터 인덱스를 보고하는 방문 예산 decoder를 작성하세요.
힌트 보기 마지막 셀은 표현된 cdr가 빈 목록인 포인터입니다. 순환 안전 순회는 두 필드를 따라가기 전에 포인터 인덱스를 기억해야 합니다.