4.4.6 · 질의 시스템 구현 unification은 assertion과 이름을 바꾼 rule을 하나의 프레임 프로토콜로 잇는다. 양쪽 변수 unification, rule 변수 이름 바꾸기, assertion·rule 탐색, conjunction, disjunction, negation-as-failure, instantiate, 명시적인 rule 깊이 예산을 구현합니다.
생각해 볼 질문
저장된 사실뿐 아니라 재사용 가능한 rule에서 도출한 결론에도 질의를 맞추려면 어떤 장치가 더 필요할까요? 비교 양쪽에 나타난 변수 unify하기 프레임을 확장하기 전에 기존 변수 바인딩 따라가기 적용할 때마다 rule의 모든 변수 새 이름으로 바꾸기 rule 결론을 unify한 뒤 본문을 평가하여 rule 적용하기 프레임 목록 위에서 and·or·유한 negation-as-failure 합성하기 남은 깊이를 명시하여 재귀 rule 적용 제한하기 unify-match는 단순 assertion matching을 일반화합니다. 어느 쪽도 변수가 될 수 있고 기존 바인딩은 비교가 끝나기 전에 또 다른 변수나 구조로 이어질 수 있습니다. rule을 적용할 때마다 rename-rule은 각 논리 변수를 현재 rule 적용 id를 가진 새 키로 바꿉니다. 이로써 rule의 비공개 중간 이름이 질의 변수나 같은 rule의 다른 적용과 충돌하지 않습니다.
simple-query는 직접 assertion 성공과 rule 결과를 합칩니다. rule 결과는 질의 패턴과 이름을 바꾼 결론을 unify한 뒤 그 프레임을 rule 본문의 qeval로 넘깁니다. and는 프레임을 파이프라인으로 전달하고 or는 대안을 합치며 not은 같은 프레임에서 하위 질의 결과가 없을 때만 프레임을 남깁니다. 명시적인 깊이는 rule 확장을 제한하는 정확한 유한 자원 경계입니다.
SICP 코드 UTF-8 6,009 / 1,048,576바이트
( begin
( define failed ' failed )
( define ( variable? expression )
( and ( pair? expression ) ( eq? ( car expression ) ' var ) ) )
( define ( variable-key variable ) ( cadr variable ) )
( define ( binding variable frame )
( assoc ( variable-key variable ) frame ) )
( define ( extend-if-possible variable value frame )
( let ( ( record ( binding variable frame ) ) )
( cond ( record
( unify-match ( cdr record ) value frame ) )
( ( variable? value )
( let ( ( value-record ( binding value frame ) ) )
( if value-record
( unify-match variable ( cdr value-record ) frame )
( cons ( cons ( variable-key variable ) value ) frame ) ) ) )
( else
( cons ( cons ( variable-key variable ) value ) frame ) ) ) ) )
( define ( unify-match left right frame )
( cond ( ( eq? frame failed ) failed )
( ( equal? left right ) frame )
( ( variable? left )
( extend-if-possible left right frame ) )
( ( variable? right )
( extend-if-possible right left frame ) )
( ( and ( pair? left ) ( pair? right ) )
( unify-match
( cdr left )
( cdr right )
( unify-match ( car left ) ( car right ) frame ) ) )
( else failed ) ) )
( define ( instantiate expression frame )
( cond ( ( variable? expression )
( let ( ( record ( binding expression frame ) ) )
( if record
( instantiate ( cdr record ) frame )
expression ) ) )
( ( pair? expression )
( cons ( instantiate ( car expression ) frame )
( instantiate ( cdr expression ) frame ) ) )
( else expression ) ) )
( define rename-counter 0 )
( define rename-table ' ( ) )
( define ( fresh-variable variable )
( let ( ( record ( assoc ( variable-key variable ) rename-table ) ) )
( if record
( cdr record )
( let ( ( fresh
( list ' var
( list ( variable-key variable )
rename-counter ) ) ) )
( set! rename-table
( cons ( cons ( variable-key variable ) fresh )
rename-table ) )
fresh ) ) ) )
( define ( rename-rule rule )
( set! rename-counter ( + rename-counter 1 ) )
( set! rename-table ' ( ) )
( define ( rename expression )
( cond ( ( variable? expression )
( fresh-variable expression ) )
( ( pair? expression )
( cons ( rename ( car expression ) )
( rename ( cdr expression ) ) ) )
( else expression ) ) )
( list ' rule
( rename ( cadr rule ) )
( rename ( caddr rule ) ) ) )
( define assertions
' ( ( parent alice bob )
( parent bob carol )
( parent bob dave )
( parent carol erin )
( job alice engineer )
( job bob manager )
( job carol researcher ) ) )
( define rules
' ( ( rule
( grandparent ( var x ) ( var z ) )
( and ( parent ( var x ) ( var y ) )
( parent ( var y ) ( var z ) ) ) ) ) )
( define ( assertion-results pattern frames )
( define ( for-frame frame records )
( if ( null? records )
' ( )
( let ( ( result
( unify-match pattern ( car records ) frame ) ) )
( if ( eq? result failed )
( for-frame frame ( cdr records ) )
( cons result
( for-frame frame ( cdr records ) ) ) ) ) ) )
( if ( null? frames )
' ( )
( append ( for-frame ( car frames ) assertions )
( assertion-results pattern ( cdr frames ) ) ) ) )
( define ( rule-results pattern frames depth )
( define ( for-frame frame remaining-rules )
( if ( or ( = depth 0 ) ( null? remaining-rules ) )
' ( )
( let* ( ( renamed ( rename-rule ( car remaining-rules ) ) )
( conclusion ( cadr renamed ) )
( body ( caddr renamed ) )
( unified ( unify-match pattern conclusion frame ) ) )
( append
( if ( eq? unified failed )
' ( )
( qeval body ( list unified ) ( - depth 1 ) ) )
( for-frame frame ( cdr remaining-rules ) ) ) ) ) )
( if ( null? frames )
' ( )
( append ( for-frame ( car frames ) rules )
( rule-results pattern ( cdr frames ) depth ) ) ) )
( define ( simple-query pattern frames depth )
( append ( assertion-results pattern frames )
( rule-results pattern frames depth ) ) )
( define ( conjoin queries frames depth )
( if ( null? queries )
frames
( conjoin
( cdr queries )
( qeval ( car queries ) frames depth )
depth ) ) )
( define ( disjoin queries frames depth )
( if ( null? queries )
' ( )
( append ( qeval ( car queries ) frames depth )
( disjoin ( cdr queries ) frames depth ) ) ) )
( define ( negate query frames depth )
( if ( null? frames )
' ( )
( append
( if ( null? ( qeval query ( list ( car frames ) ) depth ) )
( list ( car frames ) )
' ( ) )
( negate query ( cdr frames ) depth ) ) ) )
( define ( qeval query frames depth )
( cond ( ( and ( pair? query ) ( eq? ( car query ) ' and ) )
( conjoin ( cdr query ) frames depth ) )
( ( and ( pair? query ) ( eq? ( car query ) ' or ) )
( disjoin ( cdr query ) frames depth ) )
( ( and ( pair? query ) ( eq? ( car query ) ' not ) )
( negate ( cadr query ) frames depth ) )
( else
( simple-query query frames depth ) ) ) )
( define grandparent-frames
( qeval
' ( grandparent alice ( var who ) )
( list ' ( ) )
4 ) )
( define filtered-child-frames
( qeval
' ( and
( parent bob ( var child ) )
( not ( parent ( var child ) erin ) ) )
( list ' ( ) )
4 ) )
( define alternative-frames
( qeval
' ( or ( job alice engineer )
( job carol researcher ) )
( list ' ( ) )
4 ) )
( list
( map ( lambda ( frame )
( instantiate ' ( grandchild ( var who ) ) frame ) )
grandparent-frames )
( map ( lambda ( frame )
( instantiate ' ( var child ) frame ) )
filtered-child-frames )
( length alternative-frames ) ) ) 코드 실행Ctrl/⌘ Enter 파일 열기 코드 저장 코드 복사 편집기 지우기
예제 grandparent를 도출하고 논리 형식을 합성하기
실행은 브라우저 안에서 이루어지며 프로그램 결과와 실행 추적을 보여줍니다. 예상 결과 엔진은 (((grandchild carol) (grandchild dave)) (dave) 2)를 반환합니다.
실행 추적에서 볼 점 질의 패턴이 직접 assertion matching과 이름을 바꾼 각 rule 결론으로 들어가는 경로를 따라가세요. grandparent 본문이 conjoin으로 들어가기 전에 fresh 변수 키를 살펴보세요. 부정한 parent 질의에서 carol은 거부되고 dave는 남는 이유를 찾고 or 두 갈래가 같은 빈 입력 프레임에서 시작하는지 확인하세요.
직접 해보기 프로그램을 수정하고 결과를 비교해 보세요. 직접 parent 절과 재귀 절을 가진 ancestor rule을 추가하세요. 깊이 4와 깊이 1로 각각 실행해 답을 따로 보고하고 작업 예산이 결과 전선을 어떻게 정하는지 설명하세요.
힌트 보기 같은 ancestor 결론을 가진 rule 두 개를 사용하세요. 재귀 본문은 새 중간 변수 하나를 만들고 남은 깊이로 ancestor를 다시 호출해야 합니다.