# SICP Chapter 3 Exercises & Solutions

The HTML for this page has been automatically generated by parsing the loosely formatted source code & comments included in my solutions repository (https://github.com/zv/SICP-guile), if you encounter any unformatted solutions, you think you've spotted an error or you have something you'd like to share about the exercises, don't hesitate to leave a comment below (or a pull request on the source repo).

## Exercises Chapter 3

### Exercise 3.1

An accumulator is a procedure that is called repeatedly with a single numeric

argument and accumulates its arguments into a sum. Each time it is called, it

returns the currently accumulated sum. Write a procedure make-accumulator that

generates accumulators, each maintaining an independent sum. The input to

make-accumulator should specify the initial value of the sum;

(define A (make-accumulator 5))

#### Answer

(define (make-accumulator x) (lambda (n) (+ x n)))

### Exercise 3.2

In software-testing applications, it is useful to be able to count

the number of times a given procedure is called during the course of a

computation. Write a procedure make-monitored that takes as input a procedure,

f, that itself takes one input. The result returned by make-monitored is a third

procedure, say mf, that keeps track of the number of times it has been called by

maintaining an internal counter. If the input to mf is the special symbol

how-many-calls?, then mf returns the value of the counter. If the input is the

special symbol reset-count, then mf resets the counter to zero. For any other

input, mf returns the result of calling f on that input and increments the

counter. For instance, we could make a monitored version of the sqrt procedure:

#### Answer

(define (make-monitored f) (define calls 0) (lambda (n) (if (eq? n 'how-many-calls?) calls (begin (set! calls (inc calls)) (f n)))))

#### Exercise 3.3

Modify the make-account procedure so that it creates password-protected

accounts. That is, make-account should take a symbol as an additional argument,

as in

#### Answer

(define 3.3/acc (make-account 100 'secret-password)) (define (3.3/make-account acct# passwd) (define (withdraw amount) (if (>= balance amount) (begin (set! balance (- balance amount)) balance) "Insufficient funds")) (define (deposit amount) (set! balance (+ balance amount)) balance) (define (dispatch m) (cond ((eq? m 'withdraw) withdraw) ((eq? m 'deposit) deposit) (else (error "Unknown request: MAKE-ACCOUNT" m)))) dispatch)

### Exercise 3.5

Monte Carlo integration is a method of estimating definite integrals by means of

Monte Carlo simulation. Consider computing the area of a region of space

described by a predicate p ( x , y ) that is true for points ( x , y ) in the

region and #f for points not in the region. For example, the region contained

within a circle of radius 3 centered at (5, 7) is described by the predicate

that tests whether ( x − 5 ) 2 + ( y − 7 ) 2 ≤ 3 2 . To estimate the area of the

region described by such a predicate, begin by choosing a rectangle that

contains the region. For example, a rectangle with diagonally opposite corners

at (2, 4) and (8, 10) contains the circle above. The desired integral is the

area of that portion of the rectangle that lies in the region. We can estimate

the integral by picking, at random, points ( x , y ) that lie in the rectangle,

and testing p ( x , y ) for each point to determine whether the point lies in

the region. If we try this with many points, then the fraction of points that

fall in the region should give an estimate of the proportion of the rectangle

that lies in the region. Hence, multiplying this fraction by the area of the

entire rectangle should produce an estimate of the integral.

Implement Monte Carlo integration as a procedure estimate-integral that takes as

arguments a predicate p, upper and lower bounds x1, x2, y1, and y2 for the

rectangle, and the number of trials to perform in order to produce the estimate.

Your procedure should use the same monte-carlo procedure that was used above to

estimate π . Use your estimate-integral to produce an estimate of π by measuring

the area of a unit circle.

You will find it useful to have a procedure that returns a number chosen at

random from a given range. The following random-in-range procedure implements

this in terms of the random procedure used in 1.2.6, which returns a nonnegative

number less than its input.136

#### Answer

(define (random-in-range low high) ;; had to write my own (+ low (random high))) (define (estimate-integral p x1 x2 y1 y2 trials) (define width (abs (- x2 x1))) (define height (abs (- y2 y1))) (define area (* width height)) (define (iter remaining passed) (let* ((x (random-in-range x1 x2)) (y (random-in-range y1 y2)) (is-contained? (p x y))) (cond ((= remaining 0) (/ passed trials)) (is-contained? (iter (dec remaining) (inc passed))) (else (iter (dec remaining) passed))))) (* area (iter trials 0))) (define (unit-circle-pred x y) (<= (+ (* x x) (* y y)) 1))

### Exercise 3.6

It is useful to be able to reset a random-number generator to produce a sequence

starting from a given value. Design a new rand procedure that is called with an

argument that is either the symbol generate or the symbol reset and behaves as

follows: (rand 'generate) produces a new random number; ((rand 'reset)

⟨new-value⟩) resets the internal state variable to the designated ⟨new-value⟩.

Thus, by resetting the state, one can generate repeatable sequences. These are

very handy to have when testing and debugging programs that use random numbers.

#### Answer

;; This is what I assume he meant?? (define (rand command) (case command ('generate (random 10)) (else (λ (new) (seed->random-state new))))) ;; Utilities (define (count-pairs x) (if (not (pair? x)) 0 (+ (count-pairs (car x)) (count-pairs (cdr x)) 1)))

### Exercise 3.12:

The following procedure for appending lists was introduced in 2.2.1:

(define (append x y)

(if (null? x)

y

(cons (car x) (append (cdr x) y))))

Append forms a new list by successively consing the elements of x onto y. The

procedure append! is similar to append, but it is a mutator rather than a

constructor. It appends the lists by splicing them together, modifying the final

pair of x so that its cdr is now y. (It is an error to call append! with an

empty x.)

#### Answer

An infinite loop occurs (a cycle in the linked list has been made)

(define (last-pair x) (if (null? (cdr x)) x (last-pair (cdr x)))) (define x (list 'a 'b)) (define y (list 'c 'd)) (define z (append x y)) ;; Exercise 3.13 ;; What happens if we try to compute (last-pair z)? (define (make-cycle x) (set-cdr! (last-pair x) x) x)

### Exercise 3.14:

The following procedure is quite useful, although obscure:

(define (mystery x)

(define (loop x y)

(if (null? x)

y

(let ((temp (cdr x)))

(set-cdr! x y)

(loop temp x))))

(loop x '()))

Loop uses the “temporary” variable temp to hold the old value of the cdr of x,

since the set-cdr! on the next line destroys the cdr. Explain what mystery does

in general. Suppose v is defined by (define v (list 'a 'b 'c 'd)). Draw the

box-and-pointer diagram that represents the list to which v is bound. Suppose

that we now evaluate (define w (mystery v)). Draw box-and-pointer diagrams that

show the structures v and w after evaluating this expression. What would be

printed as the values of v and w?

#### Answer

Mystery requotes an array "in-place"

### Exercise 3.16

Ben Bitdiddle decides to write a procedure to count the number of pairs in any

list structure. “It’s easy,” he reasons. “The number of pairs in any structure

is the number in the car plus the number in the cdr plus one more to count the

current pair.” So Ben writes the following procedure:

(define (count-pairs x)

(if (not (pair? x))

0

(+ (count-pairs (car x))

(count-pairs (cdr x))

1)))

Show that this procedure is not correct. In particular, draw box-and-pointer

diagrams representing list structures made up of exactly three pairs for which

Ben’s procedure would return 3; return 4; return 7; never return at all.

#### Answer

(define count-three-pairs '(a b c)) (define count-four-pairs '(a b c)) (define count-seven-pairs '(a b c)) (set-car! (cdr count-four-pairs) (cdr (cdr count-four-pairs))) (set-car! count-seven-pairs (cdr count-seven-pairs)) #| Answer: (count-pairs count-three-pairs) => 3 (count-pairs count-four-pairs) => 4 (count-pairs count-seven-pairs) => 7 |#

### Exercise 3.17

Devise a correct version of the count-pairs procedure of Exercise 3.16 that

returns the number of distinct pairs in any structure.

(Hint: Traverse the structure, maintaining an auxiliary data structure that is

used to keep track of which pairs have already been counted.)

#### Answer

(define (zv-count-pairs xs) (define counted '()) (define (loop xs) (cond ((not (pair? xs)) 1) ((null? xs) 0) ((memq (car xs) counted) 0) (else (begin (set! counted (cons (car xs) counted)) (+ (loop (car xs)) (loop (cdr xs))))))) (loop xs))

### Exercise 3.18

Write a procedure that examines a list and determines whether it contains a

cycle, that is, whether a program that tried to find the end of the list by

taking successive cdrs would go into an infinite loop. Exercise 3.13 constructed

such lists.

#### Answer

(define (has-cycles? xs) (define visited '()) (define (search ys) (cond ((null? ys) #f) ((memq (car ys) visited) #t) (else (begin (set! visited (cons (car ys) visited)) (search (cdr ys)))))) (search xs))

### Exercise 3.19

Redo Exercise 3.18 using an algorithm that takes only a constant amount of

space. (This requires a very clever idea.)

#### Answer

(define* (linear-cycle-search x1 #:optional (x2 (cdr x1))) (cond ((or (null? (cdr x1)) (null? (cdr x2))) #f) ((eq? x1 x2) #t) (else (linear-cycle-search (cdr x1) (cdr (cdr x2))))))

### Exercise 3.21

Ben Bitdiddle decides to test the queue implementation described above. He types

in the procedures to the Lisp interpreter and proceeds to try them out:

“It’s all wrong!” he complains. “The interpreter’s response shows that the last

item is inserted into the queue twice. And when I delete both items, the second

b is still there, so the queue isn’t empty, even though it’s supposed to be.”

Eva Lu Ator suggests that Ben has misunderstood what is happening. “It’s not

that the items are going into the queue twice,” she explains. “It’s just that

the standard Lisp printer doesn’t know how to make sense of the queue

representation. If you want to see the queue printed correctly, you’ll have to

define your own print procedure for queues.” Explain what Eva Lu is talking

about. In particular, show why Ben’s examples produce the printed results that

they do. Define a procedure print-queue that takes a queue as input and prints

the sequence of items in the queue.

#### Answer

(define (print-queue qs) (format #t "~a~%" (car qs)))

### Exercise 3.22

Instead of representing a queue as a pair of pointers, we can build a queue as a

procedure with local state. The local state will consist of pointers to the

beginning and the end of an ordinary list. Thus, the make-queue procedure will

have the form

(define (make-queue)

(let ((front-ptr … )

(rear-ptr … ))

⟨definitions of internal procedures⟩

(define (dispatch m) …)

dispatch))

Complete the definition of make-queue and provide implementations of the queue

operations using this representation.

#### Answer

(define (make-curryq) (let ((front-ptr '()) (rear-ptr '())) (define (set-fptr! item) (set! front-ptr item)) (define (set-rptr! item) (set! rear-ptr item)) (define (empty-curryq?) (null? front-ptr)) (define (front-curryq) (if (empty-curryq?) (error "FRONT on empty queue") (car front-ptr))) (define (insert-curryq! item) (let ((new-pair (cons item '()))) (cond [(empty-curryq?) (set-fptr! item) (set-rptr! item)] [else (set! rear-ptr new-pair) (set-rptr! new-pair)]))) (define (print-queue) (format #t "~a~%" front-ptr)) (define (dispatch m) (cond [(eq? m 'front-ptr) front-ptr] [(eq? m 'rear-ptr) rear-ptr] [(eq? m 'insert-queue!) insert-curryq!] [(eq? m 'print-queue) print-queue])) dispatch))

### Exercise 3.23

A deque (“double-ended queue”) is a sequence in which items can be inserted and

deleted at either the front or the rear. Operations on deques are the

constructor make-deque, the predicate empty-deque?, selectors front-deque and

rear-deque, and mutators front-insert-deque!, rear-insert-deque!,

front-delete-deque!, rear-delete-deque!. Show how to represent deques using

pairs, and give implementations of the operations. All operations should be

accomplished in Θ(1) steps.

#### Answer

This is the structure I've decided to use for the deque. There may be other

neat ways to encode a deque with cons-cells. I'd love to hear if anyone has

a better structure:

F: Front Ptr

B: Back Ptr

X: Value

/: Null or End

~~---~~—+

| F | B |------------–—+

~~-|-~~—+ |

V V

~~-~~-~~---~~ ~~---~~—+ ~~-~~-~~---~~

| * | * |–>| * | * |–>| * | / |

~~-|-~~—+ ~~-|-~~—+ ~~-|-~~—+

V ^—+ V ^—+ V

~~-~~-~~---~~ | ~~---~~—+ | ~~---~~—+

| X | / | | | X | * | | | X | * |

~~---~~—+ | ~~---~~-~~-~~ | ~~---~~-~~-~~

| | | |

~~-------~~ ~~-------~~

(define (make-deque) '(() . ()))

(define (empty-deque? dq) (null? (front-deque dq)))

(define (front-deque dq) (car dq))

(define (rear-deque dq) (cdr dq))

(define (next-deque lst) (if (null? lst) '() (cdr lst)))

(define (prev-deque lst) (if (null? lst) '() (cdar lst)))

(define (front-insert-deque! dq value)

(let ([new-elt (cons (cons value '()) '())])

(cond

((empty-deque? dq)

(set-car! dq new-elt) (set-cdr! dq new-elt)

dq)

(else

;; link our next element to the current front

(set-cdr! new-elt (front-deque dq))

;; find the next element to make a backwards link

(set-cdr! (car (front-deque dq)) new-elt)

(set-car! dq new-elt)

dq))))

(define (rear-insert-deque! dq value)

(let ([new-elt (cons (cons value '()) '())])

(cond

((empty-deque? dq)

(set-car! dq new-elt) (set-cdr! dq new-elt)

dq)

(else

;; Link our backwards element

(set-cdr! (car new-elt) (rear-deque dq))

(set-cdr! (rear-deque dq) new-elt)

(set-cdr! dq new-elt)

dq))))

(define (front-delete-deque! dq)

(let ([next (next-deque (front-deque dq))]

[front (front-deque dq)])

(cond

((null? next) (set-car! dq '()) (set-cdr! dq '()))

(else

(set-car! dq next)

(set-cdr! (car (front-deque dq)) '())))

front))

(define (rear-delete-deque! dq)

(let ([rear (rear-deque dq)]

[prev (prev-deque (rear-deque dq))])

(cond

((null? rear) (set-car! dq '()) (set-cdr! dq '()))

(else

(set-cdr! dq prev)

(set-cdr! (rear-deque dq) '())))

rear))

### Exercise 3.24:

In the table implementations above, the keys are tested for equality using

`equal?' (called by `assoc'). This is not always the appropriate test. For

instance, we might have a table with numeric keys in which we don't need an

exact match to the number we're looking up, but only a number within some

tolerance of it. Design a table constructor `make-table' that takes as an

argument a `same-key?' procedure that will be used to test "equality" of

keys. `Make-table' should return a `dispatch' procedure that can be used to

access appropriate `lookup' and `insert!' procedures for a local table.

#### Answer

(define (make-table-with-key same-key?) (let ((local-table (list '*table*))) ;; just redefine `assoc' with `same-key?' (define (assoc key records) (cond ((null? records) #f) ((same-key? key (caar records)) (car records)) (else (assoc key (cdr records))))) (define (lookup key-1 key-2) (let ((subtable (assoc key-1 (cdr local-table)))) (if subtable (let ((record (assoc key-2 (cdr subtable)))) (if record (cdr record) #f)) #f))) (define (insert! key-1 key-2 value) (let ((subtable (assoc key-1 (cdr local-table)))) (if subtable (let ((record (assoc key-2 (cdr subtable)))) (if record (set-cdr! record value) (set-cdr! subtable (cons (cons key-2 value) (cdr subtable))))) (set-cdr! local-table (cons (list key-1 (cons key-2 value)) (cdr local-table)))))) (define (dispatch m) (cond ((eq? m 'lookup-proc) lookup) ((eq? m 'insert-proc!) insert!))) dispatch))

### Exercise 3.25

Generalizing one and two-dimensional tables, show how to implement a table

in which values are stored under an arbitrary number of keys and different

values may be stored under different numbers of keys. The `lookup' and

`insert!' procedures should take as input a list of keys used to access the

table.

#### Answer

The easiest way to accomplish this is to accept variadic arguments to `insert' and `lookup', folding them into a string or using the list directly (which `equal?' can compare)

### Exercise 3.26

To search a table as implemented above, one needs to scan through the list

of records. This is basically the unordered list representation of section

*Note 2-3-3. For large tables, it may be more efficient to structure the

table in a different manner. Describe a table implementation where the

(key, value) records are organized using a binary tree, assuming that keys

can be ordered in some way (e.g., numerically or alphabetically). (Compare

Exercise 2-66 of Chapter 2)

#### Answer:

The value that is to be inserted is converted into it's numeric form. Insert & Lookup function as you would expect

### Exercise 3.27

"Memoization" (also called "tabulation") is a technique that enables a

procedure to record, in a local table, values that have previously been

computed. This technique can make a vast difference in the performance of a

program. A memoized procedure maintains a table in which values of previous

calls are stored using as keys the arguments that produced the values. When

the memoized procedure is asked to compute a value, it first checks the

table to see if the value is already there and, if so, just returns that

value. Otherwise, it computes the new value in the ordinary way and stores

this in the table. As an example of memoization, recall from section

1-2-2 the exponential process for computing Fibonacci numbers:

(define (fib n)

(cond ((= n 0) 0)

((= n 1) 1)

(else (+ (fib (- n 1))

(fib (- n 2))))))

The memoized version of the same procedure is

(define memo-fib

(memoize (lambda (n)

(cond ((= n 0) 0)

((= n 1) 1)

(else (+ (memo-fib (- n 1))

(memo-fib (- n 2))))))))

where the memoizer is defined as

(define (memoize f)

(let ((table (make-table)))

(lambda (x)

(let ((previously-computed-result (lookup x table)))

(or previously-computed-result

(let ((result (f x)))

(insert! x result table)

result))))))

Draw an environment diagram to analyze the computation of `(memo-fib

3)'. Explain why `memo-fib' computes the nth Fibonacci number in a number

of steps proportional to n. Would the scheme still work if we had simply

defined `memo-fib' to be `(memoize fib)'?

#### Answer:

memo-fib is O(N) because the fibonacci sequence can simply be computed in 2*(Σ(N)) steps (half of which are 'precomputed') The scheme would not work if each function were freshly memoized because the `table' would not be shared between the various applications of `memo-fib'.

### Exercise 3.28

Define an or-gate as a primitive function box. Your or-gate constructor should

be similar to and-gate.

#### Answer

(define or-gate-delay 5) (define (or-gate a1 a2 output) (define (or-action-procedure) (let ((new-value (logior (signal-value a1) (signal-value a2)))) (after-delay or-gate-delay (λ () (set-signal! output new-value))))) (add-action! a1 or-action-procedure) (add-action! a2 or-action-procedure) 'ok)

### Exercise 3.29

Another way to construct an or-gate is as a compound digital logic device, built

from and-gates and inverters. Define a procedure or-gate that accomplishes this.

What is the delay time of the or-gate in terms of and-gate-delay and

inverter-delay?

#### Answer

(!a1) && a2 is congruent to a1 || a2, it is as fast as (AND-DELAY + INVERTER_{DELAY})

### TODO Exercise 3.30

Figure 3.27 shows a ripple-carry adder formed by stringing together n

full-adders. This is the simplest form of parallel adder for adding two n -bit

binary numbers. The inputs A 1 , A 2 , A 3 , …, A n and B 1 , B 2 , B 3 , …, B n

are the two binary numbers to be added (each A k and B k is a 0 or a 1). The

circuit generates S 1 , S 2 , S 3 , …, S n , the n bits of the sum, and C , the

carry from the addition. Write a procedure ripple-carry-adder that generates

this circuit. The procedure should take as arguments three lists of n wires

each—the A k , the B k , and the S k —and also another wire C . The major

drawback of the ripple-carry adder is the need to wait for the carry signals to

propagate. What is the delay needed to obtain the complete output from an n -bit

ripple-carry adder, expressed in terms of the delays for and-gates, or-gates,

and inverters?

### Exercise 3.31

The internal procedure `accept-action-procedure!' defined in make-wire specifies

that when a new action procedure is added to a wire, the procedure is

immediately run. Explain why this initialization is necessary. In particular,

trace through the half-adder example in the paragraphs above and say how the

system’s response would differ if we had defined accept-action-procedure! as

(define (accept-action-procedure! proc)

(set! action-procedures (cons proc action-procedures)))

#### Answer:

the signal value must be initialized or the entire system will run the action

procedures (no matter what has changed)

### TODO Exercise 3.32

The procedures to be run during each time segment of the agenda are kept in a

queue. Thus, the procedures for each segment are called in the order in which

they were added to the agenda (first in, first out). Explain why this order must

be used. In particular, trace the behavior of an and-gate whose inputs change

from 0, 1 to 1, 0 in the same segment and say how the behavior would differ if

we stored a segment’s procedures in an ordinary list, adding and removing

procedures only at the front (last in, first out).

### Exercise 3.33

Using primitive multiplier, adder, and constant constraints, define a procedure

averager that takes three connectors a, b, and c as inputs and establishes the

constraint that the value of c is the average of the values of a and b.

#### Answer

(define (averager a b c) (with-connectors (half sum) (constant 0.5 half) (adder a b sum) (multiplier half sum c) 'ok))

### Exercise 3.34

Louis Reasoner wants to build a squarer, a constraint device with two terminals

such that the value of connector b on the second terminal will always be the

square of the value a on the first terminal. He proposes the following simple

device made from a multiplier:

(define (squarer a b) (multiplier a a b))

There is a serious flaw in this idea. Explain.

#### Answer

The value of `a' is not "duplicated" across – so `process-new-value' only reads either `rhs' or `lhs''s value

### Exercise 3.35

Ben Bitdiddle tells Louis that one way to avoid the trouble in

Exercise 3.34 is to define a squarer as a new primitive constraint. Fill in the

missing portions in Ben’s outline for a procedure to implement such a

constraint:

(define (squarer a b)

(define (process-new-value)

(if (has-value? b)

(if (< (get-value b) 0)

(error "square less than 0:

SQUARER"

(get-value b))

⟨alternative1⟩)

⟨alternative2⟩))

(define (process-forget-value) ⟨body1⟩)

(define (me request) ⟨body2⟩)

⟨rest of definition⟩

me)

#### Answer

(define-class <squarer> (<constraint>) ;; in squarer, there is essentially only one value (rhs #:allocation #:virtual #:slot-ref (lambda (o) (slot-ref o 'lhs)) #:slot-set! (lambda (o s) (slot-set! o 'lhs s))) ;; strictly speaking these are not nessasary, as they are defined directly in ;; `process-new-value', they are kept for posteriety. (operator #:init-value square) (inverse-operator #:init-value sqrt)) (define-method (process-new-value (c <squarer>)) (let* ([lhs-conn (lhs c)] [total-conn (total c)] [has-total? (has-value? total-conn)] [has-lhs? (has-value? lhs-conn)]) ;; Determine what values *are* known and set the appropriate connector. (cond ;; (lhs < 0) => error [(and (has-lhs? (< (connector-value lhs-conn) 0))) (error "square less than 0: SQUARER" (connector-value lhs-conn))] ;; lhs = √total [has-total? (set-value! lhs-conn (sqrt (connector-value total-conn)) c)] ;; total = lh² [has-lhs? (set-value! total-conn (square (connector-value lhs-conn)) c)]))) (define-method (process-forget-value (c <squarer>)) (forget-value! (lhs c) c) (forget-value! (total c) c) (process-new-value c))

### TODO Exercise 3.36

Suppose we evaluate the following sequence of expressions in the global

environment:

(define a (make-connector)) (define b (make-connector)) (set-value! a 10 'user)

At some time during evaluation of the set-value!, the following expression from

the connector’s local procedure is evaluated:

(for-each-except setter inform-about-value constraints)

Draw an environment diagram showing the environment in which the above

expression is evaluated.

### Exercise 3.37

The celsius-fahrenheit-converter procedure is cumbersome when compared with a

more expression-oriented style of definition, such as

(define (celsius-fahrenheit-converter x)

(c+ (c* (c/ (cv 9) (cv 5)) x)

(cv 32)))

(define C (make-connector))

(define F (celsius-fahrenheit-converter C))

Here c+, c*, etc. are the “constraint” versions of the arithmetic operations.

For example, c+ takes two connectors as arguments and returns a connector that

is related to these by an adder constraint:

(define (c+ x y)

(let ((z (make-connector))) (adder x y z) z))

Define analogous procedures c-, c*, c/, and cv (constant value) that enable us

to define compound constraints as in the converter example above.

#### Answer

(define (c+ augend addend) (with-connectors (sum) (adder augend addend sum) sum)) (define (c- minuend subtrahend) (with-connectors (difference) (adder difference subtrahend minuend) difference)) (define (c* multiplicand m2) (with-connectors (product) (multiplier multiplicand m2 product) product)) (define (c/ dividend divisor) (with-connectors (quotient) (multiplier quotient divisor dividend) quotient)) (define (cv value) (with-connectors (result) (constant value result) result))

### Exercise 3.38

Suppose that Peter, Paul, and Mary share a joint bank account that initially

contains $100. Concurrently, Peter deposits $10, Paul withdraws $20, and Mary

withdraws half the money in the account, by executing the following commands:

Peter: (set! balance (+ balance 10))

Paul: (set! balance (- balance 20))

Mary: (set! balance (- balance (/ balance 2)))

1. List all the different possible values for balance after these three

transactions have been completed, assuming that the banking system forces the

three processes to run sequentially in some order.

2. What are some other values that could be produced if the system allows the

processes to be interleaved? Draw timing diagrams like the one in Figure 3.29 to

explain how these values can occur.

#### Answer

Peter->Paul->Mary: (calc-eval "((100+10)-20)/2") => 45

Peter->Mary->Paul: (calc-eval "((100+10)/2)-20") => 35

Mary->Paul->Peter: (calc-eval "((100 / 2) - 20) + 20") => 50

Mary->Peter->Paul: (calc-eval "((100 / 2) + 10) - 20") => 40

Paul->Peter->Mary: (calc-eval "((100 - 20) + 10) / 2") => 45

Paul->Mary->Peter: (calc-eval "((100 - 20) / 2) + 10") => 50

### Exercise 3.39

Which of the five possibilities in the parallel execution shown above remain if

we instead serialize execution as follows:

(define x 10)

(define s (make-serializer))

(parallel-execute

(lambda ()

(set! x ((s (lambda () (* x x))))))

(s (lambda () (set! x (+ x 1)))))

#### Answer

101, 100, 121

### Exercise 3.40

Give all possible values of x that can result from executing

(define x 10)

(parallel-execute

(lambda () (set! x (* x x)))

(lambda () (set! x (* x x x))))

Which of these possibilities remain if we instead use serialized procedures:

(define x 10)

(define s (make-serializer))

(parallel-execute

(s (lambda () (set! x (* x x))))

(s (lambda () (set! x (* x x x)))))

#### Answer

x*6 (multiplication is commutative)

### Exercise 3.41

Ben Bitdiddle worries that it would be better to implement the bank account as

follows (where the commented line has been changed):

(define (make-account balance)

(define (withdraw amount)

(if (>= balance amount)

(begin

(set! balance

(- balance amount))

balance)

"Insufficient funds"))

(define (deposit amount)

(set! balance (+ balance amount))

balance)

(let ((protected (make-serializer)))

(define (dispatch m)

(cond ((eq? m 'withdraw)

(protected withdraw))

((eq? m 'deposit)

(protected deposit))

((eq? m 'balance)

((protected

(lambda ()

balance)))) ; serialized

(else

(error

"Unknown request:

MAKE-ACCOUNT"

m))))

dispatch))

because allowing unserialized access to the bank balance can result in anomalous

behavior. Do you agree? Is there any scenario that demonstrates Ben’s concern?

#### Answer

First off, this question is phrased in a really shitty way. Second off, no

BALANCEis safe. the other functions areUNSAFE

### Exercise 3.42

Ben Bitdiddle suggests that it’s a waste of time to create a new serialized

procedure in response to every withdraw and deposit message. He says that

make-account could be changed so that the calls to protected are done outside

the dispatch procedure. That is, an account would return the same serialized

procedure (which was created at the same time as the account) each time it is

asked for a withdrawal procedure.

(define (make-account balance)

(define (withdraw amount)

(if (>= balance amount)

(begin (set! balance

(- balance amount))

balance)

"Insufficient funds"))

(define (deposit amount)

(set! balance (+ balance amount))

balance)

(let ((protected (make-serializer)))

(let ((protected-withdraw

(protected withdraw))

(protected-deposit

(protected deposit)))

(define (dispatch m)

(cond ((eq? m 'withdraw)

protected-withdraw)

((eq? m 'deposit)

protected-deposit)

((eq? m 'balance)

balance)

(else

(error "Unknown request: MAKE-ACCOUNT"

m))))

dispatch)))

#### Answer

This is fine

### TODO Exercise 3.43

Suppose that the balances in three accounts start out as $10, $20, and $30,

and that multiple processes run, exchanging the balances in the accounts.

Argue that if the processes are run sequentially, after any number of

concurrent exchanges, the account balances should be $10, $20, and $30 in

some order. Draw a timing diagram like the one in *Note Figure 3-29:: to

show how this condition can be violated if the exchanges are implemented

using the first version of the account-exchange program in this section. On

the other hand, argue that even with this `exchange' program, the sum of

the balances in the accounts will be preserved. Draw a timing diagram to

show how even this condition would be violated if we did not serialize the

transactions on individual accounts.

#### Answer

SKIPPED: Timing diagram

### TODO Exercise 3.47

A semaphore (of size n ) is a generalization of a mutex. Like a mutex, a

semaphore supports acquire and release operations, but it is more general in

that up to n processes can acquire it concurrently. Additional processes that

attempt to acquire the semaphore must wait for release operations. Give

implementations of semaphores

1. In terms of mutexes

2. In terms of atomic test-and-set! operations.

## Comments

You can add a comment to this page by writing one on it's Github Issue Page