I’ve implemented the amb
evaluator fully before starting with section 4.3 – it can be downloaded here
Exercise 4.50
I will use the existing amb
, passing it a shuffled list of choices:
(defun ramb? (exp) (tagged-list? exp 'ramb)) (defun analyze-ramb (exp) (analyze-amb (cons 'ramb (shuffle-list (amb-choices exp)))))
shuffle-list
is this naive1 procedure:
(defun shuffle-list (lst) (sort lst #'(lambda (x y) (zerop (random 2)))))
And finally, this has to be added to the cond
in analyze.
:
((ramb? exp) (analyze-ramb exp))
Exercise 4.51
Adding this to the cond
in analyze.
:
((permanent-set? exp) (analyze-permanent-set exp))
And this is the implementation:
(defun permanent-set? (exp) (tagged-list? exp 'permanent-set!)) (defun analyze-permanent-set (exp) (let ((var (assignment-variable exp)) (vproc (analyze. (assignment-value exp)))) (lambda (env succeed fail) (funcall vproc env (lambda (val fail2) (set-variable-value! var val env) (funcall succeed 'ok (lambda () (funcall fail2)))) fail))))
Note that it’s very similar to analyze-assignment, except that it doesn’t roll back the old value in the fail continuation passed to vproc
.
Exercise 4.52
(defun analyze-if-fail (exp) (let ((pproc (analyze. (if-predicate exp))) (cproc (analyze. (if-consequent exp)))) (lambda (env succeed fail) (funcall pproc env (lambda (pred-value fail2) (if (true? pred-value) pred-value (funcall fail))) (lambda () (funcall cproc env succeed fail))))))
With the usual additions to the evaluator:
(defun if-fail? (exp) (tagged-list? exp 'if-fail))
And into analyze.
:
((if-fail? exp) (analyze-if-fail exp))
Exercise 4.53
It prints:
((8 35) (3 110) (3 20))
Although the let
form always fails (it calls (amb)
as its last statement), the pairs get added into pairs
, because permanent-set!
doesn’t roll assignments back from failed paths.
Exercise 4.54
(defun analyze-s-require (exp) (let ((pproc (analyze. (s-require-predicate exp)))) (lambda (env succeed fail) (funcall pproc env (lambda (pred-value fail2) (if (not (true? pred-value)) (funcall fail) (funcall succeed 'ok fail2))) fail))))
1 It’s naive because it’s inefficient and doesn’t produce a perfect shuffle. Rather, the shuffle depends on the sorting algorithm. However, for our needs here, this shuffle is fine.