명시적인 평가기는 guest 평가 전체에 성공 continuation과 실패 continuation을 함께 전달할 수 있습니다. amb는 남은 선택을 실패 경로에 저장하고 require는 후보가 제약을 어기면 그 경로를 호출합니다.
생각해 볼 질문
평가기는 실패를 종료 오류가 아니라 이전 선택점의 다음 대안을 다시 시작하라는 요청으로 어떻게 바꿀까요?
명시적인 성공·실패 continuation으로 guest 식 평가하기
어휘 환경을 가진 guest 복합 프로시저 나타내기
연산자와 피연산자 평가 전체에 대안 continuation 전달하기
남은 선택으로 이어지는 실패 경로를 사용해 amb 구현하기
술어가 거짓일 때 현재 대안을 호출해 require 구현하기
모든 유한 해를 모으거나 명시적인 해 수 한도에서 멈추기
빠진 대입 되돌리기와 공정성 정책을 경계로 남기기
ambeval은 expression과 environment와 succeed와 fail을 받습니다. 결정적인 식은 값과 함께 나중 일이 그 값을 거부할 때 사용할 실패 continuation을 succeed에 넘깁니다. amb 형식은 첫 선택을 평가하면서 실패 continuation을 남은 선택을 시도하는 프로시저로 바꿉니다. 피연산자 평가는 이 continuation을 왼쪽에서 오른쪽으로 이어 전달하므로 프로시저 본문의 실패가 앞에서 고른 비결정적 인자의 다음 값으로 돌아갈 수 있습니다.
require는 같은 continuation 체계 안에서 술어를 평가합니다. 참이면 ok로 성공하고 거짓이면 다음 술어 대안을 호출하며 결국 가장 가까운 amb 선택점으로 돌아갑니다. all-values는 성공마다 받은 다음 대안을 계속 호출합니다. 첫 실행은 선택한 유한 탐색을 모두 소진해 complete를 보고하고 두 번째는 해 네 개 뒤에 일부러 멈춰 truncated를 보고합니다. 수업용 평가기는 순서가 있는 amb 선택과 require와 프로시저 적용과 성공 또는 실패 continuation 이동을 다룹니다.
SICP 코드UTF-8 7,667 / 1,048,576바이트
(begin(define(tagged-list?expressiontag)(and(pair?expression)(eq?(carexpression)tag)))(define(self-evaluating?expression)(or(number?expression)(string?expression)(boolean?expression)))(define(quoted?expression)(tagged-list?expression'quote))(define(if?expression)(tagged-list?expression'if))(define(lambda?expression)(tagged-list?expression'lambda))(define(begin?expression)(tagged-list?expression'begin))(define(amb?expression)(tagged-list?expression'amb))(define(require?expression)(tagged-list?expression'require))(define(text-of-quotationexpression)(cadrexpression))(define(if-predicateexpression)(cadrexpression))(define(if-consequentexpression)(caddrexpression))(define(if-alternativeexpression)(cadddrexpression))(define(lambda-parametersexpression)(cadrexpression))(define(lambda-bodyexpression)(cddrexpression))(define(begin-actionsexpression)(cdrexpression))(define(amb-choicesexpression)(cdrexpression))(define(require-predicateexpression)(cadrexpression))(define(operatorexpression)(carexpression))(define(operandsexpression)(cdrexpression))(define(pair-bindingsvariablesvalues)(cond((and(null?variables)(null?values))'())((null?variables)(error"too many arguments"))((null?values)(error"too few arguments"))(else(cons(cons(carvariables)(carvalues))(pair-bindings(cdrvariables)(cdrvalues))))))(define(extend-environmentvariablesvaluesenvironment)(append(pair-bindingsvariablesvalues)environment))(define(lookup-variable-valuevariableenvironment)(let((binding(assocvariableenvironment)))(ifbinding(cdrbinding)(error"unbound guest variable"variable))))(define(make-procedureparametersbodyenvironment)(list'compoundparametersbodyenvironment))(define(compound-procedure?procedure)(tagged-list?procedure'compound))(define(procedure-parametersprocedure)(cadrprocedure))(define(procedure-bodyprocedure)(caddrprocedure))(define(procedure-environmentprocedure)(cadddrprocedure))(define(eval-sequenceexpressionsenvironmentsucceedfail)(cond((null?expressions)(succeed'okfail))((null?(cdrexpressions))(ambeval(carexpressions)environmentsucceedfail))(else(ambeval(carexpressions)environment(lambda(ignorednext-alternative)(eval-sequence(cdrexpressions)environmentsucceednext-alternative))fail))))(define(get-argumentsexpressionsenvironmentsucceedfail)(if(null?expressions)(succeed'()fail)(ambeval(carexpressions)environment(lambda(argumentnext-argument)(get-arguments(cdrexpressions)environment(lambda(remainingnext-remaining)(succeed(consargumentremaining)next-remaining))next-argument))fail)))(define(apply-procedureprocedureargumentssucceedfail)(cond((procedure?procedure)(succeed(applyprocedurearguments)fail))((compound-procedure?procedure)(eval-sequence(procedure-bodyprocedure)(extend-environment(procedure-parametersprocedure)arguments(procedure-environmentprocedure))succeedfail))(else(error"not a guest procedure"procedure))))(define(eval-ifexpressionenvironmentsucceedfail)(ambeval(if-predicateexpression)environment(lambda(predicate-valuenext-predicate)(ambeval(ifpredicate-value(if-consequentexpression)(if-alternativeexpression))environmentsucceednext-predicate))fail))(define(eval-requireexpressionenvironmentsucceedfail)(ambeval(require-predicateexpression)environment(lambda(predicate-valuenext-predicate)(ifpredicate-value(succeed'oknext-predicate)(next-predicate)))fail))(define(eval-ambchoicesenvironmentsucceedfail)(define(try-nextremaining)(if(null?remaining)(fail)(ambeval(carremaining)environmentsucceed(lambda()(try-next(cdrremaining))))))(try-nextchoices))(define(eval-applicationexpressionenvironmentsucceedfail)(ambeval(operatorexpression)environment(lambda(procedurenext-operator)(get-arguments(operandsexpression)environment(lambda(argumentsnext-arguments)(apply-procedureprocedureargumentssucceednext-arguments))next-operator))fail))(define(ambevalexpressionenvironmentsucceedfail)(cond((self-evaluating?expression)(succeedexpressionfail))((symbol?expression)(succeed(lookup-variable-valueexpressionenvironment)fail))((quoted?expression)(succeed(text-of-quotationexpression)fail))((if?expression)(eval-ifexpressionenvironmentsucceedfail))((lambda?expression)(succeed(make-procedure(lambda-parametersexpression)(lambda-bodyexpression)environment)fail))((begin?expression)(eval-sequence(begin-actionsexpression)environmentsucceedfail))((amb?expression)(eval-amb(amb-choicesexpression)environmentsucceedfail))((require?expression)(eval-requireexpressionenvironmentsucceedfail))((pair?expression)(eval-applicationexpressionenvironmentsucceedfail))(else(error"unknown guest expression"expression))))(defineprimitive-environment(list(cons'++)(cons'--)(cons'**)(cons'//)(cons'==)(cons'<<)(cons'>>)(cons'<=<=)(cons'>=>=)(cons'listlist)(cons'conscons)(cons'carcar)(cons'cdrcdr)(cons'null?null?)(cons'pair?pair?)(cons'notnot)(cons'absabs)))(define(all-valuesexpressionsolution-limit)(if(=solution-limit0)(list'truncated'())(let((answers'())(count0))(ambevalexpressionprimitive-environment(lambda(valuenext-alternative)(set!answers(consvalueanswers))(set!count(+count1))(if(=countsolution-limit)(list'truncated(reverseanswers))(next-alternative)))(lambda()(list'complete(reverseanswers)))))))(definepair-program'((lambda(leftright)(begin(require(<leftright))(require(=(+leftright)5))(listleftright)))(amb1234)(amb1234)))(all-valuespair-program20))
예제
결과—
출력
—
값
—
진단
—
실행 추적0 / 0 개 이벤트
예상 결과
순서쌍 탐색은 (complete ((1 4) (2 3)))을 반환합니다. 한도를 둔 세 수 탐색은 (truncated ((1 2 3) (1 2 4) (1 2 5) (1 3 4)))를 반환합니다.
실행 추적에서 볼 점
각 amb 선택이 남은 선택을 위한 실패 continuation을 설치하는 곳을 찾으세요. 그다음 본문의 require가 후보를 거부할 때 get-arguments가 뒤의 third 인자와 second 인자와 first 인자의 다음 대안으로 차례로 돌아가는 과정을 따라가세요. 탐색은 해가 소진되거나 설정한 해 수 한도에 도달할 때 멈춥니다.
직접 해보기
프로그램을 수정하고 결과를 비교해 보세요.
순서쌍 프로그램의 left와 right를 1부터 6에서 고르고 곱이 12이며 left가 더 작은 모든 해를 모으도록 바꾸세요. 실행 전에 해의 순서를 예상하세요.
힌트 보기
왼쪽에서 오른쪽으로 진행하는 amb 순서는 left가 1일 때 모든 right를 시험한 뒤 left 2로 이동합니다. 두 require를 모두 만족하는 순서가 있는 약수쌍만 남습니다.