2
ThemeThis lecture explores a language feature that
provides for nondeterministic computing. It allows a declarative style of programming, that supports the “generate and test” paradigm, where the programmer provides what is to be computed and the underlying language implementation provides the control mechanism (search with backtracking) to carry out the computation.
Implementation relies on “continuations” which encapsulate control flow choices.
3
Outline
Motivating exampleAmb and searchReview of analyzing interpreterContinuations and an implementation of the
Amb Interpreter
4
Motivating Example(define (prime-sum-pair list1 list2)
(let ((a (an-element-of list1))(b (an-element-of list2)))
(require (prime? (+ a b)))(list a b)))
5
Motivating Example(define (search-prime-pairs L1 L2)
(define (search-fixed x L)(if (null? L) null
(if (prime? (+ x (first L)))(list x (first L) (+ x (first L)))(search-fixed x (rest L)))))
(if (null? L1)'()(let ((p (search-fixed (first L1) L2)))
(if (not (null? p)) p(search-prime-pairs (rest L1) L2))))) ;;; backtrack
> (search-prime-pairs '(1 3 5 8) '(20 35 110))'(3 20 23)
6
Motivating Example(define (search-all-prime-pairs L1 L2)
(define (search-fixed x L)(if (null? L) null
(if (prime? (+ x (first L)))(cons (list x (first L) (+ x (first L)))
(search-fixed x (rest L))) ;;; continue search(search-fixed x (rest L)))))
(if (null? L1) '()(let ((P (search-fixed (first L1) L2)))
(append P (search-all-prime-pairs (rest L1) L2)))))
> (search-all-prime-pairs '(1 3 5 8) '(20 35 110))'((3 20 23) (3 110 113) (8 35 43))
7
Motivating Example(define (all-pairs L1 L2)
(if (or (null? L1)(null? L2))null(append(map (lambda (x) (list (first L1) x)) L2)(all-pairs (rest L1) L2))))
> (filter (lambda (p) (prime? (+ (first p) (second p)))) (all-pairs '(1 3 5 8) '(20 35 110)))
'((3 20) (3 110) (8 35))
8
Motivating Example(define (all-pairs-streams L1 L2)
(if (or (stream-empty? L1)(stream-empty? L2))empty-stream(stream-append(stream-map (lambda (x) (list (stream-first L1) x)) L2)
(all-pairs-streams (stream-rest L1) L2))))
> (stream-first (stream-filter (lambda (p) (prime? (+ (first p) (second p)))) (all-pairs-streams '(1 3 5 8) '(20 35 110))))
'(3 20)
9
Motivating Example(define (prime-sum-pair list1 list2)
(let ((a (an-element-of list1))(b (an-element-of list2)))
(require (prime? (+ a b)))(list a b)))
;;; Amb-Eval input:(prime-sum-pair '(1 3 5 8) '(20 35 110));;; Starting a new problem;;; Amb-Eval value:(3 20)
10
Motivating Example;;; Amb-Eval input:(prime-sum-pair '(1 3 5 8) '(20 35 110));;; Starting a new problem;;; Amb-Eval value:(3 20);;; Amb-Eval input:try-again;;; Amb-Eval value:(3 110);;; Amb-Eval input:try-again;;; Amb-Eval value:(8 35)
;;; Amb-Eval input:try-again;;; There are no more values of(prime-sum-pair (quote (1 3 5 8)) (quote (20 35 110)));;; Amb-Eval input:(prime-sum-pair '(19 27 30) '(11 36 58));;; Starting a new problem;;; Amb-Eval value:(30 11)
11
Amb Construct
The (amb <e1> … <en>) “ambiguously” returns one of the values <ei>
Example(list (amb 1 2 3) (amb 'a 'b)) Chooses one of the following (1 a) (1 b) (2 a) (2 b) (3 a) (3 b)
12
Nondeterministic Branching
amb splits the computation into branches where computation continues on each branch with one possible value. amb is a non-deterministic choice point Failure causes backtrackingImplementation uses depth-first seach
“chronological backtracking”Driver loop can be prompted with “try-
again” to continue search for more solutions
13
Nondeterministic Selection(define (require p)
(if (not p) (amb))) ;;; (amb) fails
(define (an-element-of items)(require (not (null? items)))(amb (car items) (an-element-of (cdr items))))
(define (an-integer-starting-from n)(amb n (an-integer-starting-from (+ n 1))))
(define (naturals) (an-integer-starting-from 0))
Backtracking
(prime-sum-pair '(1 3 5 8) '(20 35 110))
(amb 1 3 5 8)
(amb 20 35 110) (amb 20 35 110) (amb 20 35 110) (amb 20 35 110)
1 3 58
2035
110
15
Logic ConstraintsBaker, Cooper, Fletcher, Miller, and Smith live on different floors of an apartment house that contains only five floors. Baker does not live on the top floor. Cooper does not live on the bottom floor. Fletcher does not live on either the top or the bottom floor. Miller lives on a higher floor than does Cooper. Smith does not live on a floor adjacent to Fletcher's. Fletcher does not live on a floor adjacent to Cooper's. Where does everyone live?
16
Logic Constraints(define (multiple-dwelling)
(let ((baker (amb 1 2 3 4 5))(cooper (amb 1 2 3 4 5))(fletcher (amb 1 2 3 4 5))(miller (amb 1 2 3 4 5))(smith (amb 1 2 3 4 5)))
(require(distinct? (list baker cooper fletcher miller smith)))
(require (not (= baker 5)))(require (not (= cooper 1)))(require (not (= fletcher 5)))(require (not (= fletcher 1)))(require (> miller cooper))(require (not (= (abs (- smith fletcher)) 1)))(require (not (= (abs (- fletcher cooper)) 1)))(list (list 'baker baker)
(list 'cooper cooper)(list 'fletcher fletcher)(list 'miller miller)(list 'smith smith))))
(multiple-dwelling)
=>
((baker 3) (cooper 2) (fletcher 4) (miller 5) (smith 1))
17
Implementation
Modify analyzing evaluatorEach execution procedure now takes three
arguments and environment and two functions called continuations to complete the computation Success continuation Failure continuation
If a value is returned the success continuation is called with that valueIf the evaluation results in a dead end, the failure
continuation is called
18
Continuations
The job of the success continuation is to receive a value and proceed with the computationThe job of the failure continuation is to try
another branchMay result from (amb)Try another non-deterministic choiceIf none, a failure at an earlier choice point is
triggered
19
Structure of Amb-eval A success continuation is a procedure of two
arguments: the value just obtained and another failure continuation to be used if that value leads to a subsequent failure.
A failure continuation is a procedure of no arguments
(define (ambeval exp env succeed fail)((analyze exp) env succeed fail))
(ambeval <exp>the-global-environment(lambda (value fail) value)(lambda () 'failed))
21
Creating Iterators(define (make-it L)
(let ((Lp L))(lambda ()
(if (null? Lp)'fail(let ((val (first Lp)))
(begin (set! Lp (rest Lp)) val))))))
(define (until iter done?)(let ((p (iter)))(if (eq? p 'fail)
'fail(if (done? p) p (until iter done?)))))
22
Using Iterators> (define next (make-it '(12 18 21)))> (next)12> (next)18> (next)21> (next)'fail> (define next (make-it '(12 18 21 23 33 37)))> (until next prime?)23
23
Motivating Example(define (it-pairs L1 L2)
(let ((L1p L1) (L2p L2))(lambda ()(if (or (null? L1) (null? L2)) 'fail(if (not (null? L2p))
(let ((val (list (first L1p) (first L2p))))(begin (set! L2p (rest L2p)) val))
(begin (set! L2p L2) (set! L1p (rest L1p))(if (null? L1p)
'fail(let ((val (list (first L1p) (first L2p))))(begin (set! L2p (rest L2p)) val)))))))))
24
Motivating Example> (define next-pair (it-pairs '(1 3 5 8) '(20 35 110)))
> (until next-pair (lambda (p) (prime? (+ (first p) (second p)))))
'(3 20)
25
Iterator with Continuation(define (make-continue-it L testp)
(let ((Lp L)) (lambda (succeed fail)(define (try-next Lp)
(if (null? Lp)(fail)(testp (first Lp) succeed (lambda () (try-next (cdr Lp))))))
(try-next L))))
26
Using Continuations(define (testodd? x succeed fail)
(if (odd? x) (succeed x fail) (fail)))
> (define it (make-continue-it '(2 5 4 7) testodd?))> (it (lambda (val fail) val) (lambda () 'fail))5> (it (lambda (val fail) (display val) (newline) (fail)) (lambda () 'fail))57'fail> (it (lambda (val fail) (cons val (fail))) (lambda () '()))'(5 7)
28
Analyzing Interpreter(define (eval exp env)((analyze exp) env))
(define (analyze exp)(cond ((self-evaluating? exp)
(analyze-self-evaluating exp))((quoted? exp) (analyze-quoted exp))((variable? exp) (analyze-variable exp))((assignment? exp) (analyze-assignment exp))((definition? exp) (analyze-definition exp))((if? exp) (analyze-if exp))((lambda? exp) (analyze-lambda exp))((begin? exp) (analyze-sequence (begin-actions exp)))((cond? exp) (analyze (cond->if exp)))((application? exp) (analyze-application exp))(else(error "Unknown expression type -- ANALYZE" exp))))
29
Simple Expressions(define (analyze-self-evaluating exp)(lambda (env) exp))
(define (analyze-variable exp)(lambda (env) (lookup-variable-value exp env)))
(define (analyze-assignment exp)(let ((var (assignment-variable exp))
(vproc (analyze (assignment-value exp))))(lambda (env)(set-variable-value! var (vproc env) env)'ok)))
30
Conditional(define (analyze-if exp)(let ((pproc (analyze (if-predicate exp)))
(cproc (analyze (if-consequent exp)))(aproc (analyze (if-alternative exp))))
(lambda (env)(if (true? (pproc env))
(cproc env)(aproc env)))))
(define (analyze-lambda exp)(let ((vars (lambda-parameters exp))
(bproc (analyze-sequence (lambda-body exp))))(lambda (env) (make-procedure vars bproc env))))
; analyze-sequence composes the functions in the sequence
31
Amb Interpreter(define (ambeval exp env succeed fail)((analyze exp) env succeed fail))
(define (analyze exp)(cond ((self-evaluating? exp)
(analyze-self-evaluating exp))((quoted? exp) (analyze-quoted exp))((variable? exp) (analyze-variable exp))((assignment? exp) (analyze-assignment exp))((definition? exp) (analyze-definition exp))((if? exp) (analyze-if exp))((lambda? exp) (analyze-lambda exp))((begin? exp) (analyze-sequence (begin-actions exp)))((cond? exp) (analyze (cond->if exp)))((let? exp) (analyze (let->combination exp))) ;**((amb? exp) (analyze-amb exp)) ;**((application? exp) (analyze-application exp))(else(error "Unknown expression type -- ANALYZE" exp))))
32
Calling Amb Interpreter(define (ambeval exp env succeed fail)((analyze exp) env succeed fail))
(ambeval expthe-global-environment(lambda (val fail) val)(lambda () ‘fail))
33
Simple Expressions(define (analyze-self-evaluating exp)
(lambda (env succeed fail)(succeed exp fail)))
(define (analyze-variable exp)(lambda (env succeed fail)(succeed (lookup-variable-value exp env)
fail)))
34
Conditionals(define (analyze-if exp)
(let ((pproc (analyze (if-predicate exp)))(cproc (analyze (if-consequent exp)))(aproc (analyze (if-alternative exp))))
(lambda (env succeed fail)(pproc env
;; success continuation for evaluating the predicate;; to obtain pred-value(lambda (pred-value fail2)
(if (true? pred-value)(cproc env succeed fail2)(aproc env succeed fail2)))
;; failure continuation for evaluating the predicatefail))))
35
Assignment(define (analyze-assignment exp)(let ((var (assignment-variable exp))
(vproc (analyze (assignment-value exp))))(lambda (env succeed fail)(vproc env
(lambda (val fail2) ; *1*(let ((old-value
(lookup-variable-value var env))) (set-variable-value! var val env)(succeed 'ok
(lambda () ; *2*(set-variable-value! var
old-valueenv)
(fail2)))))fail))))
36
Amb(define (analyze-amb exp)
(let ((cprocs (map analyze (amb-choices exp))))(lambda (env succeed fail)
(define (try-next choices)(if (null? choices)
(fail)((car choices) env
succeed(lambda ()
(try-next (cdr choices))))))(try-next cprocs))))
37
Driver Loop(define (driver-loop)(define (internal-loop try-again)
(prompt-for-input input-prompt)(let ((input (read)))(if (eq? input 'try-again)
(try-again)(begin(newline)(display ";;; Starting a new problem ")(ambeval input
the-global-environment;; ambeval success(lambda (val next-alternative)(announce-output output-prompt)(user-print val)(internal-loop next-alternative))
;; ambeval failure(lambda ()(announce-output";;; There are no more values of")(user-print input)(driver-loop)))))))
(internal-loop(lambda ()(newline)(display ";;; There is no current problem")(driver-loop))))
Top Related