Skip to content

Commit 057aef1

Browse files
committed
Add implementation of the constraint system
1 parent 9b6ba7e commit 057aef1

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed

chapter3/constraints.rkt

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#lang racket
2+
(provide (all-defined-out))
3+
4+
(define (has-value? connector)
5+
(connector 'has-value?))
6+
7+
(define (get-value connector)
8+
(connector 'value))
9+
10+
(define (set-value! connector new-value informant)
11+
((connector 'set-value!) new-value informant))
12+
13+
(define (forget-value! connector retractor)
14+
((connector 'forget) retractor))
15+
16+
(define (connect connector new-constraint)
17+
((connector 'connect) new-constraint))
18+
19+
(define (inform-about-value constraint)
20+
(constraint 'I-have-a-value))
21+
22+
(define (inform-about-no-value constraint)
23+
(constraint 'I-lost-my-value))
24+
25+
(define (adder a1 a2 sum)
26+
(define (process-new-value)
27+
(cond ((and (has-value? a1)
28+
(has-value? a2))
29+
(set-value! sum (+ (get-value a1)
30+
(get-value a2))
31+
me))
32+
((and (has-value? a1)
33+
(has-value? sum))
34+
(set-value! a2 (- (get-value sum)
35+
(get-value a1))
36+
me))
37+
((and (has-value? a2) (has-value? sum))
38+
(set-value! a1
39+
(- (get-value sum)
40+
(get-value a2))
41+
me))))
42+
(define (process-forget-value)
43+
(forget-value! sum me)
44+
(forget-value! a1 me)
45+
(forget-value! a2 me)
46+
(process-new-value))
47+
48+
(define (me request)
49+
(cond ((eq? request 'I-have-a-value)
50+
(process-new-value))
51+
((eq? request 'I-lost-my-value)
52+
(process-forget-value))
53+
(else
54+
(error "Unknown request -- ADDER" request))))
55+
(connect a1 me)
56+
(connect a2 me)
57+
(connect sum me)
58+
me)
59+
60+
(define (probe name connector)
61+
(define (print-probe value)
62+
(newline)
63+
(display "Probe: ")
64+
(display name)
65+
(display " = ")
66+
(display value))
67+
68+
(define (process-new-value)
69+
(print-probe (get-value connector)))
70+
71+
(define (process-forget-value)
72+
(print-probe "?"))
73+
74+
(define (me request)
75+
(cond ((eq? request 'I-have-a-value)
76+
(process-new-value))
77+
78+
((eq? request 'I-lost-my-value)
79+
(process-forget-value))
80+
(else
81+
(error "Unknown request -- PROBE" request))))
82+
(connect connector me)
83+
me)
84+
85+
(define (make-connector)
86+
(let ((value false)
87+
(informant false)
88+
(constraints '()))
89+
(define (set-my-value newval setter)
90+
(cond ((not (has-value? me))
91+
(set! value newval)
92+
(set! informant setter)
93+
(for-each-except setter
94+
inform-about-value
95+
constraints))
96+
((not (= value newval))
97+
(error "Contradiction" (list value newval)))
98+
(else 'ignored)))
99+
(define (forget-my-value retractor)
100+
(if (eq? retractor informant)
101+
(begin (set! informant false)
102+
(for-each-except retractor
103+
inform-about-no-value
104+
constraints))
105+
'ignore))
106+
107+
(define (connect new-constraint)
108+
(when (not (memq new-constraint constraints))
109+
(set! constraints
110+
(cons new-constraint constraints)))
111+
(when (has-value? me)
112+
(inform-about-value new-constraint))
113+
'done)
114+
115+
(define (me request)
116+
(cond ((eq? request 'has-value?)
117+
(if informant true false))
118+
((eq? request 'value) value)
119+
((eq? request 'set-value!) set-my-value)
120+
((eq? request 'forget) forget-my-value)
121+
((eq? request 'connect) connect)
122+
(else (error "Unknown operation -- CONNECTOR" request))))
123+
me))
124+
125+
(define (multiplier m1 m2 product)
126+
(define (process-new-value)
127+
(cond ((or (and (has-value? m1) (= (get-value m1) 0))
128+
(and (has-value? m2) (= (get-value m2) 0)))
129+
(set-value! product 0 me))
130+
((and (has-value? m1) (has-value? m2))
131+
(set-value! product
132+
(* (get-value m1)
133+
(get-value m2))
134+
me))
135+
((and (has-value? product) (has-value? m1))
136+
(set-value! m2
137+
(/ (get-value product)
138+
(get-value m1))
139+
me))
140+
((and (has-value? product) (has-value? m2))
141+
(set-value! m1
142+
(/ (get-value product)
143+
(get-value m2))
144+
me))))
145+
146+
(define (process-forget-value)
147+
(forget-value! product me)
148+
(forget-value! m1 me)
149+
(forget-value! m2 me)
150+
(process-new-value))
151+
152+
(define (me request)
153+
(cond ((eq? request 'I-have-a-value)
154+
(process-new-value))
155+
156+
((eq? request 'I-lost-my-value)
157+
(process-forget-value))
158+
159+
(else
160+
(error "Unknown request -- MULTIPLIER" request))))
161+
(connect m1 me)
162+
(connect m2 me)
163+
(connect product me)
164+
me)
165+
166+
(define (constant value connector)
167+
(define (me request)
168+
(error "Unknown request -- CONSTANT" request))
169+
(connect connector me)
170+
(set-value! connector value me)
171+
me)
172+
173+
(define (for-each-except exception procedure list)
174+
(define (loop items)
175+
(cond ((null? items) 'done)
176+
((eq? (car items) exception)
177+
(loop (cdr items)))
178+
(else (procedure (car items))
179+
(loop (cdr items)))))
180+
(loop list))
181+
182+
(define (celsius-fahrenheit-converter c f)
183+
(let ((u (make-connector))
184+
(v (make-connector))
185+
(w (make-connector))
186+
(x (make-connector))
187+
(y (make-connector)))
188+
(multiplier c w u)
189+
(multiplier v x u)
190+
(adder v y f)
191+
(constant 9 w)
192+
(constant 5 x)
193+
(constant 32 y)
194+
'ok))
195+
196+
(module+ test
197+
(require rackunit)
198+
199+
(test-case "Test for for-each-except"
200+
(define test-list '(a b c d e))
201+
(define results '())
202+
(for-each-except 'c (λ (x)
203+
(set! results (append results (list x))))
204+
test-list
205+
)
206+
(check-equal? results '(a b d e))
207+
)
208+
209+
(test-case "Test for celsius-fahrenheit-converter"
210+
(define C (make-connector))
211+
(define F (make-connector))
212+
(celsius-fahrenheit-converter C F)
213+
(probe "Celsius temp" C)
214+
(probe "Fahrenheit temp" F)
215+
(set-value! C 25 'user)
216+
(check-eq? (get-value F) 77)
217+
(check-exn (regexp "Contradiction") (lambda () (set-value! F 212 'user)))
218+
(forget-value! C 'user)
219+
(check-false (has-value? C))
220+
(set-value! F 212 'user)
221+
(check-equal? (get-value C) 100)
222+
)
223+
)

0 commit comments

Comments
 (0)