|
| 1 | +#lang racket |
| 2 | + |
| 3 | +;;; Helper procedure to call all procedures in a list |
| 4 | +(define (call-each procedures) |
| 5 | + (if (null? procedures) |
| 6 | + 'done |
| 7 | + (begin ((car procedures)) |
| 8 | + (call-each (cdr procedures))))) |
| 9 | + |
| 10 | +(define (make-wire) |
| 11 | + (let ((signal-value 0) |
| 12 | + (action-procedures '())) |
| 13 | + (define (set-my-signal! new-value) |
| 14 | + (if (not (= signal-value new-value)) |
| 15 | + (begin (set! signal-value new-value) |
| 16 | + (call-each action-procedures)) |
| 17 | + 'done)) |
| 18 | + |
| 19 | + (define (accept-action-procedure! proc) |
| 20 | + (set! action-procedures (cons proc action-procedures)) |
| 21 | + ;; Call the procedure immediately |
| 22 | + (proc)) |
| 23 | + |
| 24 | + (define (dispatch m) |
| 25 | + (cond ((eq? m 'get-signal) signal-value) |
| 26 | + ((eq? m 'set-signal!) set-my-signal!) |
| 27 | + ((eq? m 'add-action!) accept-action-procedure!) |
| 28 | + (else (error "Unknown operation -- WIRE" m)))) |
| 29 | + dispatch) |
| 30 | + ) |
| 31 | + |
| 32 | +(define (get-signal wire) |
| 33 | + (wire 'get-signal)) |
| 34 | + |
| 35 | +(define (set-signal! wire new-value) |
| 36 | + ((wire 'set-signal!) new-value)) |
| 37 | + |
| 38 | +(define (add-action! wire action-proc) |
| 39 | + ((wire 'add-action!) action-proc)) |
| 40 | + |
| 41 | +(module+ test |
| 42 | + (require rackunit) |
| 43 | + |
| 44 | + (test-case "Test for make-wire" |
| 45 | + (define a (make-wire)) |
| 46 | + (define b (make-wire)) |
| 47 | + (check-eq? (get-signal a) 0) |
| 48 | + (set-signal! a 1) |
| 49 | + (check-eq? (get-signal a) 1) |
| 50 | + ) |
| 51 | + (test-case "Test for add-action!" |
| 52 | + (define w (make-wire)) |
| 53 | + (define counter 0) |
| 54 | + (define last-signal #f) |
| 55 | + |
| 56 | + ; Add an action that increments counter and records the signal |
| 57 | + (add-action! w (lambda () |
| 58 | + (set! counter (+ counter 1)) |
| 59 | + (set! last-signal (get-signal w)))) |
| 60 | + |
| 61 | + ; The action should be called immediately when added |
| 62 | + (check-eq? counter 1) |
| 63 | + (check-eq? last-signal 0) |
| 64 | + |
| 65 | + ; Change the signal - action should be called again |
| 66 | + (set-signal! w 1) |
| 67 | + (check-eq? counter 2) |
| 68 | + (check-eq? last-signal 1) |
| 69 | + |
| 70 | + ; Change signal back - action should be called again |
| 71 | + (set-signal! w 0) |
| 72 | + (check-eq? counter 3) |
| 73 | + (check-eq? last-signal 0) |
| 74 | + |
| 75 | + ; Setting same signal should NOT call action |
| 76 | + (set-signal! w 0) |
| 77 | + (check-eq? counter 3) ; Should remain 3 |
| 78 | + (check-eq? last-signal 0) |
| 79 | + ) |
| 80 | + ) |
0 commit comments