생각해 볼 질문
세 값 가운데 어떤 두 값이 먼저 와도 하나의 관계가 올바르게 반응하려면 무엇이 필요할까요? 커넥터 값과 제공자 및 제약 리스트를 명시적으로 표현하기 지식이 나타나거나 사라질 때 연결된 제약들에 알리기 adder 관계를 사용해 세 값 중 어느 하나를 유도하기 독립적인 사용자 값을 지우지 않고 의존하는 지식만 철회하기 각 커넥터는 값의 보유 여부, 값, 값을 제공한 대상, 알림을 보낼 제약들을 저장합니다. set-value!는 첫 번째 값을 받아들이고 setter를 제외한 모든 제약에 new-value를 보냅니다. 따라서 adder는 두 항으로부터 total을 계산하거나, total과 다른 한 항으로부터 나머지 한 항을 계산할 수 있습니다.
forget-value!는 철회자가 저장된 제공자일 때만 성공합니다. 사용자가 total을 잊으면 adder는 자신이 유도하여 제공한 값인 right를 철회하지만, 독립적으로 제공된 left 값은 철회할 수 없습니다. 그 후 새로운 right 값을 제공하면 같은 관계를 통해 새로운 total이 유도됩니다.
SICP 코드 UTF-8 2,377 / 1,048,576바이트
( begin
( define ( make-connector ) ( vector #f 0 #f ' ( ) ) )
( define ( has-value? connector ) ( vector-ref connector 0 ) )
( define ( get-value connector ) ( vector-ref connector 1 ) )
( define ( inform-about-except exception message constraints )
( if ( null? constraints )
' done
( begin
( if ( eq? exception ( car constraints ) )
#f
( ( car constraints ) message ) )
( inform-about-except exception message ( cdr constraints ) ) ) ) )
( define ( set-value! connector value setter )
( if ( has-value? connector )
( if ( = ( get-value connector ) value ) ' ignored ' contradiction )
( begin
( vector-set! connector 0 #t )
( vector-set! connector 1 value )
( vector-set! connector 2 setter )
( inform-about-except setter ' new-value ( vector-ref connector 3 ) )
' set ) ) )
( define ( forget-value! connector retractor )
( if ( eq? retractor ( vector-ref connector 2 ) )
( begin
( vector-set! connector 0 #f )
( vector-set! connector 2 #f )
( inform-about-except retractor ' lost-value ( vector-ref connector 3 ) )
' forgotten )
' ignored ) )
( define ( connect connector constraint )
( vector-set! connector 3 ( cons constraint ( vector-ref connector 3 ) ) )
( if ( has-value? connector ) ( constraint ' new-value ) #f )
' connected )
( define ( adder left right total )
( define ( new-value )
( cond ( ( and ( has-value? left ) ( has-value? right ) )
( set-value! total ( + ( get-value left ) ( get-value right ) ) me ) )
( ( and ( has-value? left ) ( has-value? total ) )
( set-value! right ( - ( get-value total ) ( get-value left ) ) me ) )
( ( and ( has-value? right ) ( has-value? total ) )
( set-value! left ( - ( get-value total ) ( get-value right ) ) me ) )
( else ' waiting ) ) )
( define ( lost-value )
( forget-value! left me )
( forget-value! right me )
( forget-value! total me )
( new-value ) )
( define ( me message )
( if ( eq? message ' new-value ) ( new-value ) ( lost-value ) ) )
( connect left me )
( connect right me )
( connect total me )
me )
( define left ( make-connector ) )
( define right ( make-connector ) )
( define total ( make-connector ) )
( adder left right total )
( set-value! left 10 ' user )
( set-value! right 32 ' user )
( list ( get-value left ) ( get-value right ) ( get-value total ) ) ) 코드 실행Ctrl/⌘ Enter 파일 열기 코드 저장 코드 복사 편집기 지우기
예제 두 항으로부터 합 유도하기 값을 잊고 다시 전파하기
실행은 브라우저 안에서 이루어지며 프로그램 결과와 실행 추적을 보여줍니다. 예상 결과 첫 번째 프로그램은 (10 32 42)를 반환합니다. 두 번째 프로그램은 ((10 32 42) (#t #f #f) 15)를 반환합니다.
실행 추적에서 볼 점 각 커넥터의 통지가 adder로 이어지는 과정을 따라가세요. 두 번째 실행에서는 사용자가 소유한 left 값과 adder가 소유한 right 값을 구분하고, 새로운 total이 전파되기 전에 lost-value 메시지가 유도된 경로만 철회하는 과정을 관찰하세요.
직접 해보기 프로그램을 수정하고 결과를 비교해 보세요. 두 번째 프로그램에서 total 대신 left를 잊어 보세요. 어느 커넥터가 값을 잃고 어떤 사용자 제공 값이 남는지 예측해 보세요.
힌트 보기 커넥터는 현재 제공자로부터의 철회만 받아들입니다. adder는 자신이 제공한 값만 철회할 수 있으며, user가 제공한 값은 철회할 수 없습니다.