#lang racket
(require racket/mpair)
;;;;;;;;;;;;;;;;
;; CONNECTORS ;;
;;;;;;;;;;;;;;;;
(define (make-connector)
(define value false)
(define informant false)
(define constraints empty)
(define (set-my-value newval setter)
(cond [(not (has-value? me))
(set! value newval)
(set! informant setter)
(for-each-except setter
inform-about-value
constraints)]
[(not (= value newval))
(error "Contradiction" (list value newval))]
[else 'ignored]))
(define (forget-my-value retractor)
(cond [(eq? retractor informant)
(set! informant false)
(for-each-except retractor
inform-about-no-value
constraints)]
[else 'ignored]))
(define (connect new-constraint)
(when (not (memq new-constraint constraints))
(set! constraints (cons new-constraint constraints)))
(when (has-value? me)
(inform-about-value new-constraint)
'done))
(define (me request)
(cond [(eq? request 'has-value?)
(if informant true false)]
[(eq? request 'value) value]
[(eq? request 'set-value!) set-my-value]
[(eq? request 'forget) forget-my-value]
[(eq? request 'connect) connect]
[else (error "Unknown operation -- CONNECTOR" request)]))
me)
(define (for-each-except exception procedure lst)
(map procedure (remove exception lst))
'done)
(define (has-value? connector) (connector 'has-value?))
(define (get-value connector) (connector 'value))
(define (set-value! connector new-value informant)
((connector 'set-value!) new-value informant))
(define (forget-value! connector retractor)
((connector 'forget) retractor))
(define (connect connector new-constraint)
((connector 'connect) new-constraint))
;;;;;;;;;;;;;;;;;
;; CONSTRAINTS ;;
;;;;;;;;;;;;;;;;;
(define (inform-about-value constraint)
(constraint 'I-have-a-value))
(define (inform-about-no-value constraint)
(constraint 'I-lost-my-value))
(define (adder a1 a2 sum)
(define (process-new-value)
(cond [(and (has-value? a1) (has-value? a2))
(set-value! sum
(+ (get-value a1) (get-value a2))
me)]
[(and (has-value? a1) (has-value? sum))
(set-value! a2
(- (get-value sum) (get-value a1))
me)]
[(and (has-value? a2) (has-value? sum))
(set-value! a1
(- (get-value sum) (get-value a2))
me)]))
(define (process-forget-value)
(forget-value! sum me)
(forget-value! a1 me)
(forget-value! a2 me)
(process-new-value))
(define (me request)
(cond [(eq? request 'I-have-a-value)
(process-new-value)]
[(eq? request 'I-lost-my-value)
(process-forget-value)]
[else
(error "Unknown request -- ADDER" request)]))
(connect a1 me)
(connect a2 me)
(connect sum me)
me)
(define (multiplier m1 m2 product)
(define (process-new-value)
(cond [(or (and (has-value? m1) (zero? (get-value m1)))
(and (has-value? m2) (zero? (get-value m2))))
(set-value! product 0 me)]
[(and (has-value? m1) (has-value? m2))
(set-value! product
(* (get-value m1) (get-value m2))
me)]
[(and (has-value? product) (has-value? m1))
(set-value! m2
(/ (get-value product) (get-value m1))
me)]
[(and (has-value? product) (has-value? m2))
(set-value! m1
(/ (get-value product) (get-value m2))
me)]))
(define (process-forget-value)
(forget-value! product me)
(forget-value! m1 me)
(forget-value! m2 me)
(process-new-value))
(define (me request)
(cond [(eq? request 'I-have-a-value)
(process-new-value)]
[(eq? request 'I-lost-my-value)
(process-forget-value)]
[else
(error "Unknown request -- MULTIPLIER" request)]))
(connect m1 me)
(connect m2 me)
(connect product me)
me)
(define (constant value connector)
(define (me request)
(error "Unknown request -- CONSTANT" request))
(connect connector me)
(set-value! connector value me)
me)
(define (probe name connector)
(define (print-probe value)
(printf "Probe: ~a = ~a ~n" name value))
(define (process-new-value)
(print-probe (get-value connector)))
(define (process-forget-value)
(print-probe "?"))
(define (me request)
(cond [(eq? request 'I-have-a-value)
(process-new-value)]
[(eq? request 'I-lost-my-value)
(process-forget-value)]
[else
(error "Unknown request -- PROBE" request)]))
(connect connector me)
me)
;;;;;;;;;;
;; 3.33 ;;
;;;;;;;;;;
(define (averager a b c)
(define s (make-connector))
(define t (make-connector))
(adder a b s)
(multiplier t c s)
(constant 2 t)
'ok)
;; TEST
(define A (make-connector))
(define B (make-connector))
(define C (make-connector))
(averager A B C)
;; 'ok
(probe "A" A)
;; #<procedure:me>
(probe "B" B)
;; #<procedure:me>
(probe "C" C)
;; #<procedure:me>
(set-value! A 3 'user)
;; Probe: A = 3
;; 'done
(set-value! B 5 'user)
;; Probe: B = 5
;; Probe: C = 4
;; 'done
(forget-value! B 'user)
;; Probe: B = ?
;; Probe: C = ?
;; 'done
(set-value! C 5 'user)
;; Probe: C = 5
;; Probe: B = 7
;; 'done
;;;;;;;;;;
;; 3.34 ;;
;;;;;;;;;;
(define (bad-squarer a b)
(multiplier a a b)
'ok)
;; TEST
(define E (make-connector))
(define F (make-connector))
(bad-squarer E F)
;; 'ok
(probe "E" E)
;; #<procedure:me>
(probe "F" F)
;; #<procedure:me>
(set-value! E 3 'user)
;; Probe: E = 3
;; Probe: F = 9
;; 'done
(forget-value! E 'user)
;; Probe: E = ?
;; Probe: F = ?
;; 'done
(set-value! F 25 'user)
;; Probe: F = 25
;; 'done
;; Clearing the value for a also clears the value for b. If, after that, you set
;; the value for b, multiplier has no way to propagate the value back to a.
;; Multiplier expects to have two of three values in order to propagate to the
;; third, but here it has only one. So no propagation occurs.
;;;;;;;;;;
;; 3.35 ;;
;;;;;;;;;;
(define (squarer a b)
(define (process-new-value)
(cond [(has-value? b)
(if (negative? (get-value b))
(error "Square negative -- SQUARER" (get-value b))
(set-value! a (sqrt (get-value b)) me))]
[(has-value? a)
(set-value! b (sqr (get-value a)) me)]))
(define (process-forget-value)
(forget-value! b me)
(forget-value! a me)
(process-new-value))
(define (me request)
(cond [(eq? request 'I-have-a-value)
(process-new-value)]
[(eq? request 'I-lost-my-value)
(process-forget-value)]
[else
(error "Unknown request -- SQUARER" request)]))
(connect a me)
(connect b me)
me)
;; TEST
(define G (make-connector))
(define H (make-connector))
(squarer G H)
;; #<procedure:me>
(probe "G" G)
;; #<procedure:me>
(probe "H" H)
;; #<procedure:me>
(set-value! G 3 'user)
;; Probe: G = 3
;; Probe: H = 9
;; 'done
(forget-value! G 'user)
;; Probe: G = ?
;; Probe: H = ?
;; 'done
(set-value! H 25 'user)
;; Probe: H = 25
;; Probe: G = 5
;; 'done
;; Here the constraint does give the square root as expected.
;;;;;;;;;;
;; 3.36 ;;
;;;;;;;;;;
;; Discuss the environments created by:
;; (define a (make-connector))
;; (set-value! a 10 'user)
;; make-connector is defined in the global environment. So calling it creates a
;; binding frame E1 below the global environment. It has no formal parameters to
;; bind, but it does create local definitions in E1, including set-my-value.
;; set-value! is defined in the global environment. Calling it on a, 10, and
;; 'user creates a binding frame E2, below the global environment, where connector
;; is bound to a (which is the me function in E1), new-value is bound to 10, and
;; informant is bound to 'user. Then the body of set-value! is evaluated in E2.
;; Evaluating the body of set-value! in E2 first calls connector a on 'set-value to
;; get the set-my-value function from E1, and then applies set-my-value to 10 and
;; 'user.
;; Calling set-my-value creates a new binding frame E3 below E1, where newval is
;; bound to 10 and setter is bound to 'user. Then its body is evaluated in E3,
;; and, as part of that, the for-each-except function is called.
;; Calling for-each-except creates a new binding frame E4, below its enclosing
;; environment, which is the global environment. Here in E4, the formal
;; parameters exception, procedure, and lst are bound to the setter,
;; inform-about-value, and constraints passed from E3. Then the body of
;; for-each-except is run in E4, but this means calling inform-about-value on all
;; the constraints involving a. Since the enclosing environment for
;; inform-about-value is E1, all these evaluations take place in binding frames
;; below E1 and thus have access to all a's local state.
;;;;;;;;;;
;; 3.37 ;;
;;;;;;;;;;
(define (c+ x y)
(define z (make-connector))
(adder x y z)
z)
(define (c* x y)
(define z (make-connector))
(multiplier x y z)
z)
(define (c/ x y)
(define z (make-connector))
(multiplier y z x)
z)
(define (cv value)
(define z (make-connector))
(constant value z)
z)
(define (celsius-fahrenheit-converter x)
(c+ (c* (c/ (cv 9) (cv 5))
x)
(cv 32)))
;; TEST
(define C1 (make-connector))
(define F1 (celsius-fahrenheit-converter C1))
(probe "C1" C1)
;; #<procedure:me>
(probe "F1" F1)
;; #<procedure:me>
(set-value! C1 25 'user)
;; Probe: C1 = 25
;; Probe: F1 = 77
;; 'done
(forget-value! C1 'user)
;; Probe: C1 = ?
;; Probe: F1 = ?
;; 'done
(set-value! F1 212 'user)
;; Probe: F1 = 212
;; Probe: C1 = 100
;; 'done
Comments
0 B
|👍
/👎
0 B
|👍
/👎