Compare commits

...

3 Commits

Author SHA1 Message Date
Oliver Payne 0e99db8285 Add exercise 4.36 Pythagorean triples 2023-10-24 23:04:28 +01:00
Oliver Payne 5002cfe4aa Add exercise 4.35 2023-10-20 17:30:57 +01:00
Oliver Payne 32988a58a2 Initial integration of racket version of ambeval
Some of this should probably be refactored in the same way the other
evaluators were.
2023-10-19 23:01:00 +01:00
3 changed files with 316 additions and 0 deletions

View File

@ -0,0 +1,56 @@
#lang sicp
(#%require "ambeval.rkt")
(define (require p)
(if (not p) (amb)))
(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 (a-pythagorean-triple-between low high)
(let ((i (an-integer-between low high)))
(let ((j (an-integer-between i high)))
(let ((k (an-integer-between j high)))
(require (= (+ (* i i) (* j j)) (* k k)))
(list i j k)))))
;; Exercise 4.35
(define (an-integer-between low high)
(require (<= low high))
(amb low (an-integer-between (+ low 1) high)))
;; Exercise 4.36
;; To generate all pythagorean triples, it is not sufficient to
;; replace an-integer-between with an-integer-starting-from because
;; this would attempt to search through all k, then all j and then all
;; i. As such, we'd never get to the second value of j or i.
;; Instead, we need to visit the values of i, j and k diagonally to
;; ensure that they are visited in order.
(define (a-pythagorean-triple)
(let ((t (a-triple-with-sum-from 3)))
(let ((i (car t))
(j (car (cdr t)))
(k (car (cdr (cdr t)))))
(require (= (+ (* i i) (* j j))
(* k k)))
(list i j k))))
(define (a-triple-with-sum-between low high)
(let ((i (an-integer-between 1 high)))
(let ((j (an-integer-between i (max 1 (- high i)))))
(let ((k (an-integer-between j (max 1 (- high j i)))))
(let ((sum (+ i j k)))
(require (>= sum low))
(require (< sum high))
(list i j k))))))
(define (a-triple-with-sum-from n)
(amb (a-triple-with-sum-between n (+ n 1))
(a-triple-with-sum-from (+ n 1))))

244
mceval/ambeval.rkt Normal file
View File

@ -0,0 +1,244 @@
;; Code from the book adapted for racket and my existing support code
#lang sicp
(#%require "syntax.rkt"
"environment.rkt"
"common.rkt"
"special-forms.rkt")
(#%provide ambeval
driver-loop)
(define (amb? exp) (tagged-list? exp 'amb))
(define (amb-choices exp) (cdr exp))
;; analyze from 4.1.6, with clause from 4.3.3 added
;; and also support for Let
(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))))
(define (ambeval exp env succeed fail)
((analyze exp) env succeed fail))
;;;Simple expressions
(define (analyze-self-evaluating exp)
(lambda (env succeed fail)
(succeed exp fail)))
(define (analyze-quoted exp)
(let ((qval (text-of-quotation exp)))
(lambda (env succeed fail)
(succeed qval fail))))
(define (analyze-variable exp)
(lambda (env succeed fail)
(succeed (lookup-variable-value exp env)
fail)))
(define (analyze-lambda exp)
(let ((vars (lambda-parameters exp))
(bproc (analyze-sequence (lambda-body exp))))
(lambda (env succeed fail)
(succeed (make-procedure vars bproc env)
fail))))
;;;Conditionals and sequences
(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 predicate
fail))))
(define (analyze-sequence exps)
(define (sequentially a b)
(lambda (env succeed fail)
(a env
;; success continuation for calling a
(lambda (a-value fail2)
(b env succeed fail2))
;; failure continuation for calling a
fail)))
(define (loop first-proc rest-procs)
(if (null? rest-procs)
first-proc
(loop (sequentially first-proc (car rest-procs))
(cdr rest-procs))))
(let ((procs (map analyze exps)))
(if (null? procs)
(error "Empty sequence -- ANALYZE"))
(loop (car procs) (cdr procs))))
;;;Definitions and assignments
(define (analyze-definition exp)
(let ((var (definition-variable exp))
(vproc (analyze (definition-value exp))))
(lambda (env succeed fail)
(vproc env
(lambda (val fail2)
(define-variable! var val env)
(succeed 'ok fail2))
fail))))
(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-value
env)
(fail2)))))
fail))))
;;;Procedure applications
(define (analyze-application exp)
(let ((fproc (analyze (operator exp)))
(aprocs (map analyze (operands exp))))
(lambda (env succeed fail)
(fproc env
(lambda (proc fail2)
(get-args aprocs
env
(lambda (args fail3)
(execute-application
proc args succeed fail3))
fail2))
fail))))
(define (get-args aprocs env succeed fail)
(if (null? aprocs)
(succeed '() fail)
((car aprocs) env
;; success continuation for this aproc
(lambda (arg fail2)
(get-args (cdr aprocs)
env
;; success continuation for recursive
;; call to get-args
(lambda (args fail3)
(succeed (cons arg args)
fail3))
fail2))
fail)))
(define (execute-application proc args succeed fail)
(cond ((primitive-procedure? proc)
(succeed (apply-primitive-procedure proc args)
fail))
((compound-procedure? proc)
((procedure-body proc)
(extend-environment (procedure-parameters proc)
args
(procedure-environment proc))
succeed
fail))
(else
(error
"Unknown procedure type -- EXECUTE-APPLICATION"
proc))))
;;;amb expressions
(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))))
;;;Driver loop
(define input-prompt ";;; Amb-Eval input:")
(define output-prompt ";;; Amb-Eval value:")
(define (driver-loop)
(define env (setup-environment))
(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
env
;; 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))))
;;; Support for Let (as noted in footnote 56, p.428)
(define (let? exp) (tagged-list? exp 'let))
(define (let-bindings exp) (cadr exp))
(define (let-body exp) (cddr exp))
(define (let-var binding) (car binding))
(define (let-val binding) (cadr binding))
(define (make-combination operator operands) (cons operator operands))
(define (let->combination exp)
;;make-combination defined in earlier exercise
(let ((bindings (let-bindings exp)))
(make-combination (make-lambda (map let-var bindings)
(let-body exp))
(map let-val bindings))))
'AMB-EVALUATOR-LOADED

View File

@ -159,6 +159,9 @@
(list 'cons cons)
(list 'null? null?)
(list 'list list)
(list 'memq memq)
(list 'member member)
(list 'not not)
(list '= =)
(list '+ +)
(list '* *)
@ -166,6 +169,19 @@
(list '/ /)
(list '< <)
(list '> >)
(list '+ +)
(list '- -)
(list '* *)
(list '= =)
(list '> >)
(list '>= >=)
(list '<= <=)
(list 'abs abs)
(list 'max max)
(list 'remainder remainder)
(list 'integer? integer?)
(list 'sqrt sqrt)
(list 'eq? eq?)
(list 'newline newline)
(list 'display display)
))