diff --git a/sicp/1_002e1 b/sicp/1_002e1 new file mode 100644 index 0000000..d6731ac --- /dev/null +++ b/sicp/1_002e1 @@ -0,0 +1,27 @@ + +Exercise 1.1: Below is a sequence of expressions. +What is the result printed by the interpreter in response to each expression? +Assume that the sequence is to be evaluated in the order in which it is +presented. + + +10 +(+ 5 3 4) +(- 9 1) +(/ 6 2) +(+ (* 2 4) (- 4 6)) +(define a 3) +(define b (+ a 1)) +(+ a b (* a b)) +(= a b) +(if (and (> b a) (< b (* a b))) + b + a) +(cond ((= a 4) 6) + ((= b 4) (+ 6 7 a)) + (else 25)) +(+ 2 (if (> b a) b a)) +(* (cond ((> a b) a) + ((< a b) b) + (else -1)) + (+ a 1)) diff --git a/sicp/1_002e10 b/sicp/1_002e10 new file mode 100644 index 0000000..bc98e10 --- /dev/null +++ b/sicp/1_002e10 @@ -0,0 +1,41 @@ + +Exercise 1.10: The following procedure computes +a mathematical function called Ackermann’s function. + + +(define (A x y) + (cond ((= y 0) 0) + ((= x 0) (* 2 y)) + ((= y 1) 2) + (else (A (- x 1) + (A x (- y 1)))))) + +What are the values of the following expressions? + + +(A 1 10) +(A 2 4) +(A 3 3) + +Consider the following procedures, where A is the procedure +defined above: + + +(define (f n) (A 0 n)) +(define (g n) (A 1 n)) +(define (h n) (A 2 n)) +(define (k n) (* 5 n n)) + +Give concise mathematical definitions for the functions computed by the +procedures f, g, and h for positive integer values of + + n +. For example, (k n) computes + + 5 + + n + 2 + + +. diff --git a/sicp/1_002e11 b/sicp/1_002e11 new file mode 100644 index 0000000..b1ff351 --- /dev/null +++ b/sicp/1_002e11 @@ -0,0 +1,68 @@ + +Exercise 1.11: A function + f + is defined by +the rule that + + f + ( + n + ) + = + n + + if + + n + < + 3 + + and + + f + ( + n + ) + + = + + f + ( + n + − + 1 + ) + + + + + 2 + f + ( + n + − + 2 + ) + + + + + 3 + f + ( + n + − + 3 + ) + + if + + n + ≥ + 3 + +. +Write a procedure that computes + f + by means of a recursive process. Write a procedure that +computes + f + by means of an iterative process. diff --git a/sicp/1_002e12 b/sicp/1_002e12 new file mode 100644 index 0000000..8dc4131 --- /dev/null +++ b/sicp/1_002e12 @@ -0,0 +1,17 @@ + +Exercise 1.12: The following pattern of numbers +is called +Pascal’s triangle. + + + 1 + 1 1 + 1 2 1 + 1 3 3 1 + 1 4 6 4 1 + . . . + + +The numbers at the edge of the triangle are all 1, and each number inside the +triangle is the sum of the two numbers above it.35 Write a procedure that computes elements of Pascal’s triangle by +means of a recursive process. diff --git a/sicp/1_002e13 b/sicp/1_002e13 new file mode 100644 index 0000000..656b66c --- /dev/null +++ b/sicp/1_002e13 @@ -0,0 +1,85 @@ + +Exercise 1.13: Prove that + + Fib + ( + n + ) + + is +the closest integer to + + + φ + n + + + / + + + 5 + + +, where + φ + = + + ( + 1 + + + + 5 + + ) + + / + + 2 + +. +Hint: Let + ψ + = + + ( + 1 + − + + 5 + + ) + + / + + 2 + +. +Use induction and the definition of the Fibonacci numbers (see 1.2.2) +to prove that + + Fib + ( + n + ) + + = + + ( + + φ + n + + − + + ψ + n + + ) + + / + + + 5 + + +. diff --git a/sicp/1_002e14 b/sicp/1_002e14 new file mode 100644 index 0000000..a8ba39b --- /dev/null +++ b/sicp/1_002e14 @@ -0,0 +1,5 @@ + +Exercise 1.14: Draw the tree illustrating the +process generated by the count-change procedure of 1.2.2 +in making change for 11 cents. What are the orders of growth of the space and +number of steps used by this process as the amount to be changed increases? diff --git a/sicp/1_002e15 b/sicp/1_002e15 new file mode 100644 index 0000000..b5e783c --- /dev/null +++ b/sicp/1_002e15 @@ -0,0 +1,75 @@ + +Exercise 1.15: The sine of an angle (specified +in radians) can be computed by making use of the approximation + + + sin + ⁡ + x + ≈ + x + + if + x + is sufficiently small, and the trigonometric +identity + + + + sin + ⁡ + x + + + = + + + 3 + sin + ⁡ + + x + 3 + + + + − + + + 4 + + sin + 3 + + ⁡ + + x + 3 + + + + +to reduce the size of the argument of sin. (For purposes of this +exercise an angle is considered “sufficiently small” if its magnitude is not +greater than 0.1 radians.) These ideas are incorporated in the following +procedures: + + +(define (cube x) (* x x x)) +(define (p x) (- (* 3 x) (* 4 (cube x)))) +(define (sine angle) + (if (not (> (abs angle) 0.1)) + angle + (p (sine (/ angle 3.0))))) + + + How many times is the procedure p applied when (sine 12.15) is +evaluated? + + What is the order of growth in space and number of steps (as a function of + + a +) used by the process generated by the sine procedure when +(sine a) is evaluated? + + diff --git a/sicp/1_002e16 b/sicp/1_002e16 new file mode 100644 index 0000000..e6ed3bb --- /dev/null +++ b/sicp/1_002e16 @@ -0,0 +1,67 @@ + +Exercise 1.16: Design a procedure that evolves +an iterative exponentiation process that uses successive squaring and uses a +logarithmic number of steps, as does fast-expt. (Hint: Using the +observation that + + ( + + b + + n + + / + + 2 + + + + ) + 2 + + + = + + ( + + b + 2 + + + ) + + n + + / + + 2 + + + +, keep, along with +the exponent + n + and the base + b +, an additional state variable + a +, and +define the state transformation in such a way that the product + + a + + b + n + + + +is unchanged from state to state. At the beginning of the process + + a + is taken to be 1, and the answer is given by the value of + a + at the +end of the process. In general, the technique of defining an + +invariant quantity that remains unchanged from state to state is a +powerful way to think about the design of iterative algorithms.) diff --git a/sicp/1_002e17 b/sicp/1_002e17 new file mode 100644 index 0000000..2b88ee8 --- /dev/null +++ b/sicp/1_002e17 @@ -0,0 +1,19 @@ + +Exercise 1.17: The exponentiation algorithms in +this section are based on performing exponentiation by means of repeated +multiplication. In a similar way, one can perform integer multiplication by +means of repeated addition. The following multiplication procedure (in which +it is assumed that our language can only add, not multiply) is analogous to the +expt procedure: + + +(define (* a b) + (if (= b 0) + 0 + (+ a (* a (- b 1))))) + +This algorithm takes a number of steps that is linear in b. Now suppose +we include, together with addition, operations double, which doubles an +integer, and halve, which divides an (even) integer by 2. Using these, +design a multiplication procedure analogous to fast-expt that uses a +logarithmic number of steps. diff --git a/sicp/1_002e18 b/sicp/1_002e18 new file mode 100644 index 0000000..e9b2847 --- /dev/null +++ b/sicp/1_002e18 @@ -0,0 +1,5 @@ + +Exercise 1.18: Using the results of +Exercise 1.16 and Exercise 1.17, devise a procedure that generates +an iterative process for multiplying two integers in terms of adding, doubling, +and halving and uses a logarithmic number of steps.40 diff --git a/sicp/1_002e19 b/sicp/1_002e19 new file mode 100644 index 0000000..379610f --- /dev/null +++ b/sicp/1_002e19 @@ -0,0 +1,203 @@ + +Exercise 1.19: There is a clever algorithm for +computing the Fibonacci numbers in a logarithmic number of steps. Recall the +transformation of the state variables + a + and + b + in the fib-iter +process of 1.2.2: + a + ← + a + + + b + and + b + ← + a +. +Call this transformation + T +, and observe that applying + T + over and over +again + n + times, starting with 1 and 0, produces the pair + + Fib + ( + n + + + 1 + ) + + and + + + Fib + ( + n + ) + +. In other words, the Fibonacci numbers are produced +by applying + + T + n + +, the + + n + + th + + + power of the transformation + T +, +starting with the pair (1, 0). Now consider + T + to be the special case of + + + p + = + 0 + + and + + q + = + 1 + + in a family of transformations + + T + + p + q + + +, +where + + T + + p + q + + + transforms the pair + + ( + a + , + b + ) + + according to + + a + ← + + b + q + + + + + a + q + + + + + a + p + + and + b + ← + + b + p + + + + + a + q + +. +Show that if we apply such a transformation + + T + + p + q + + + twice, the +effect is the same as using a single transformation + + T + + + p + ′ + + + q + ′ + + + + of the +same form, and compute + + p + ′ + + + and + + q + ′ + + + in terms of + p + and + q +. This +gives us an explicit way to square these transformations, and thus we can +compute + + T + n + + using successive squaring, as in the fast-expt +procedure. Put this all together to complete the following procedure, which +runs in a logarithmic number of steps:41 + + +(define (fib n) + (fib-iter 1 0 0 1 n)) + +(define (fib-iter a b p q count) + (cond ((= count 0) + b) + ((even? count) + (fib-iter a + b + ⟨??⟩ ;compute p' + ⟨??⟩ ;compute q' + (/ count 2))) + (else + (fib-iter (+ (* b q) + (* a q) + (* a p)) + (+ (* b p) + (* a q)) + p + q + (- count 1))))) diff --git a/sicp/1_002e2 b/sicp/1_002e2 new file mode 100644 index 0000000..e880323 --- /dev/null +++ b/sicp/1_002e2 @@ -0,0 +1,47 @@ + +Exercise 1.2: Translate the following expression +into prefix form: + + + + + + 5 + + + 4 + + + ( + 2 + − + ( + 3 + − + ( + 6 + + + + 4 + 5 + + ) + ) + ) + + + 3 + ( + 6 + − + 2 + ) + ( + 2 + − + 7 + ) + + + . + + + diff --git a/sicp/1_002e20 b/sicp/1_002e20 new file mode 100644 index 0000000..aadfba3 --- /dev/null +++ b/sicp/1_002e20 @@ -0,0 +1,11 @@ + +Exercise 1.20: The process that a procedure +generates is of course dependent on the rules used by the interpreter. As an +example, consider the iterative gcd procedure given above. Suppose we +were to interpret this procedure using normal-order evaluation, as discussed in +1.1.5. (The normal-order-evaluation rule for if is +described in Exercise 1.5.) Using the substitution method (for normal +order), illustrate the process generated in evaluating (gcd 206 40) and +indicate the remainder operations that are actually performed. How many +remainder operations are actually performed in the normal-order +evaluation of (gcd 206 40)? In the applicative-order evaluation? diff --git a/sicp/1_002e21 b/sicp/1_002e21 new file mode 100644 index 0000000..f1434fe --- /dev/null +++ b/sicp/1_002e21 @@ -0,0 +1,4 @@ + +Exercise 1.21: Use the smallest-divisor +procedure to find the smallest divisor of each of the following numbers: 199, +1999, 19999. diff --git a/sicp/1_002e22 b/sicp/1_002e22 new file mode 100644 index 0000000..cc7464a --- /dev/null +++ b/sicp/1_002e22 @@ -0,0 +1,67 @@ + +Exercise 1.22: Most Lisp implementations include +a primitive called runtime that returns an integer that specifies the +amount of time the system has been running (measured, for example, in +microseconds). The following timed-prime-test procedure, when called +with an integer + n +, prints + n + and checks to see if + n + is prime. If + + n + is prime, the procedure prints three asterisks followed by the amount of +time used in performing the test. + + +(define (timed-prime-test n) + (newline) + (display n) + (start-prime-test n (runtime))) + + +(define (start-prime-test n start-time) + (if (prime? n) + (report-prime (- (runtime) + start-time)))) + + +(define (report-prime elapsed-time) + (display " *** ") + (display elapsed-time)) + +Using this procedure, write a procedure search-for-primes that checks +the primality of consecutive odd integers in a specified range. Use your +procedure to find the three smallest primes larger than 1000; larger than +10,000; larger than 100,000; larger than 1,000,000. Note the time needed to +test each prime. Since the testing algorithm has order of growth of + + + Θ + ( + + n + + ) + +, you should expect that testing for primes +around 10,000 should take about + + 10 + + times as long as testing for +primes around 1000. Do your timing data bear this out? How well do the data +for 100,000 and 1,000,000 support the + + Θ + ( + + n + + ) + + prediction? Is your +result compatible with the notion that programs on your machine run in time +proportional to the number of steps required for the computation? diff --git a/sicp/1_002e23 b/sicp/1_002e23 new file mode 100644 index 0000000..16d752f --- /dev/null +++ b/sicp/1_002e23 @@ -0,0 +1,16 @@ + +Exercise 1.23: The smallest-divisor +procedure shown at the start of this section does lots of needless testing: +After it checks to see if the number is divisible by 2 there is no point in +checking to see if it is divisible by any larger even numbers. This suggests +that the values used for test-divisor should not be 2, 3, 4, 5, 6, +…, but rather 2, 3, 5, 7, 9, …. To implement this change, define a +procedure next that returns 3 if its input is equal to 2 and otherwise +returns its input plus 2. Modify the smallest-divisor procedure to use +(next test-divisor) instead of (+ test-divisor 1). With +timed-prime-test incorporating this modified version of +smallest-divisor, run the test for each of the 12 primes found in +Exercise 1.22. Since this modification halves the number of test steps, +you should expect it to run about twice as fast. Is this expectation +confirmed? If not, what is the observed ratio of the speeds of the two +algorithms, and how do you explain the fact that it is different from 2? diff --git a/sicp/1_002e24 b/sicp/1_002e24 new file mode 100644 index 0000000..3d45aa8 --- /dev/null +++ b/sicp/1_002e24 @@ -0,0 +1,17 @@ + +Exercise 1.24: Modify the +timed-prime-test procedure of Exercise 1.22 to use +fast-prime? (the Fermat method), and test each of the 12 primes you +found in that exercise. Since the Fermat test has + + Θ + ( + log + ⁡ + n + ) + + +growth, how would you expect the time to test primes near 1,000,000 to +compare with the time needed to test primes near 1000? Do your data bear this +out? Can you explain any discrepancy you find? diff --git a/sicp/1_002e25 b/sicp/1_002e25 new file mode 100644 index 0000000..1082530 --- /dev/null +++ b/sicp/1_002e25 @@ -0,0 +1,11 @@ + +Exercise 1.25: Alyssa P. Hacker complains that +we went to a lot of extra work in writing expmod. After all, she says, +since we already know how to compute exponentials, we could have simply written + + +(define (expmod base exp m) + (remainder (fast-expt base exp) m)) + +Is she correct? Would this procedure serve as well for our fast prime tester? +Explain. diff --git a/sicp/1_002e26 b/sicp/1_002e26 new file mode 100644 index 0000000..58a1a8a --- /dev/null +++ b/sicp/1_002e26 @@ -0,0 +1,42 @@ + +Exercise 1.26: Louis Reasoner is having great +difficulty doing Exercise 1.24. His fast-prime? test seems to run +more slowly than his prime? test. Louis calls his friend Eva Lu Ator +over to help. When they examine Louis’s code, they find that he has rewritten +the expmod procedure to use an explicit multiplication, rather than +calling square: + + +(define (expmod base exp m) + (cond ((= exp 0) 1) + ((even? exp) + (remainder + (* (expmod base (/ exp 2) m) + (expmod base (/ exp 2) m)) + m)) + (else + (remainder + (* base + (expmod base (- exp 1) m)) + m)))) + +“I don’t see what difference that could make,” says Louis. “I do.” says +Eva. “By writing the procedure like that, you have transformed the + + + Θ + ( + log + ⁡ + n + ) + + process into a + + Θ + ( + n + ) + + process.” +Explain. diff --git a/sicp/1_002e27 b/sicp/1_002e27 new file mode 100644 index 0000000..46423ff --- /dev/null +++ b/sicp/1_002e27 @@ -0,0 +1,23 @@ + +Exercise 1.27: Demonstrate that the Carmichael +numbers listed in Footnote 47 really do fool the Fermat test. That is, +write a procedure that takes an integer + n + and tests whether + + a + n + + is +congruent to + a + modulo + n + for every + + a + < + n + +, and try your procedure +on the given Carmichael numbers. diff --git a/sicp/1_002e28 b/sicp/1_002e28 new file mode 100644 index 0000000..e9569f8 --- /dev/null +++ b/sicp/1_002e28 @@ -0,0 +1,92 @@ + +Exercise 1.28: One variant of the Fermat test +that cannot be fooled is called the +Miller-Rabin test (Miller 1976; +Rabin 1980). This starts from an alternate form of Fermat’s Little Theorem, +which states that if + n + is a prime number and + a + is any positive integer +less than + n +, then + a + raised to the + + ( + n + − + 1 + ) + +-st power is congruent to 1 +modulo + n +. To test the primality of a number + n + by the Miller-Rabin +test, we pick a random number + + a + < + n + + and raise + a + to the + + ( + n + − + 1 + ) + +-st +power modulo + n + using the expmod procedure. However, whenever we +perform the squaring step in expmod, we check to see if we have +discovered a “nontrivial square root of 1 modulo + n +,” that is, a number +not equal to 1 or + + n + − + 1 + + whose square is equal to 1 modulo + n +. It is +possible to prove that if such a nontrivial square root of 1 exists, then + n + +is not prime. It is also possible to prove that if + n + is an odd number that +is not prime, then, for at least half the numbers + + a + < + n + +, computing + + + a + + n + − + 1 + + + in this way will reveal a nontrivial square root of 1 modulo + + n +. (This is why the Miller-Rabin test cannot be fooled.) Modify the +expmod procedure to signal if it discovers a nontrivial square root of +1, and use this to implement the Miller-Rabin test with a procedure analogous +to fermat-test. Check your procedure by testing various known primes +and non-primes. Hint: One convenient way to make expmod signal is to +have it return 0. diff --git a/sicp/1_002e29 b/sicp/1_002e29 new file mode 100644 index 0000000..2ced3df --- /dev/null +++ b/sicp/1_002e29 @@ -0,0 +1,149 @@ + +Exercise 1.29: Simpson’s Rule is a more accurate +method of numerical integration than the method illustrated above. Using +Simpson’s Rule, the integral of a function + f + between + a + and + b + is +approximated as + + + + h + 3 + + ( + + y + 0 + + + + + 4 + + y + 1 + + + + + + 2 + + y + 2 + + + + + + 4 + + y + 3 + + + + + + 2 + + y + 4 + + + + + ⋯ + + + + 2 + + y + + n + − + 2 + + + + + + + 4 + + y + + n + − + 1 + + + + + + y + n + + ) + , + + + +where + + h + = + ( + b + − + a + ) + + / + + n + +, for some even integer + n +, and + + + y + k + + = + + f + ( + a + + + k + h + ) + +. (Increasing + n + increases the +accuracy of the approximation.) Define a procedure that takes as arguments + + f +, + a +, + b +, and + n + and returns the value of the integral, computed +using Simpson’s Rule. Use your procedure to integrate cube between 0 +and 1 (with + + n + = + 100 + + and + + n + = + 1000 + +), and compare the results to those of +the integral procedure shown above. diff --git a/sicp/1_002e3 b/sicp/1_002e3 new file mode 100644 index 0000000..7185760 --- /dev/null +++ b/sicp/1_002e3 @@ -0,0 +1,4 @@ + +Exercise 1.3: Define a procedure that takes three +numbers as arguments and returns the sum of the squares of the two larger +numbers. diff --git a/sicp/1_002e30 b/sicp/1_002e30 new file mode 100644 index 0000000..36bc787 --- /dev/null +++ b/sicp/1_002e30 @@ -0,0 +1,13 @@ + +Exercise 1.30: The sum procedure above +generates a linear recursion. The procedure can be rewritten so that the sum +is performed iteratively. Show how to do this by filling in the missing +expressions in the following definition: + + +(define (sum term a next b) + (define (iter a result) + (if ⟨??⟩ + ⟨??⟩ + (iter ⟨??⟩ ⟨??⟩))) + (iter ⟨??⟩ ⟨??⟩)) diff --git a/sicp/1_002e31 b/sicp/1_002e31 new file mode 100644 index 0000000..8c93e24 --- /dev/null +++ b/sicp/1_002e31 @@ -0,0 +1,63 @@ + +Exercise 1.31: + + + The sum procedure is only the simplest of a vast number of similar +abstractions that can be captured as higher-order procedures.51 Write an analogous +procedure called product that returns the product of the values of a +function at points over a given range. Show how to define factorial in +terms of product. Also use product to compute approximations to + + π + using the formula52 + + + + π + 4 + + + = + + + + + 2 + ⋅ + 4 + ⋅ + 4 + ⋅ + 6 + ⋅ + 6 + ⋅ + 8 + ⋅ + ⋯ + + + 3 + ⋅ + 3 + ⋅ + 5 + ⋅ + 5 + ⋅ + 7 + ⋅ + 7 + ⋅ + ⋯ + + + . + + + + If your product procedure generates a recursive process, write one that +generates an iterative process. If it generates an iterative process, write +one that generates a recursive process. + + diff --git a/sicp/1_002e32 b/sicp/1_002e32 new file mode 100644 index 0000000..00f1138 --- /dev/null +++ b/sicp/1_002e32 @@ -0,0 +1,25 @@ + +Exercise 1.32: + + + Show that sum and product (Exercise 1.31) are both special +cases of a still more general notion called accumulate that combines a +collection of terms, using some general accumulation function: + + +(accumulate + combiner null-value term a next b) + +Accumulate takes as arguments the same term and range specifications as +sum and product, together with a combiner procedure (of +two arguments) that specifies how the current term is to be combined with the +accumulation of the preceding terms and a null-value that specifies what +base value to use when the terms run out. Write accumulate and show how +sum and product can both be defined as simple calls to +accumulate. + + If your accumulate procedure generates a recursive process, write one +that generates an iterative process. If it generates an iterative process, +write one that generates a recursive process. + + diff --git a/sicp/1_002e33 b/sicp/1_002e33 new file mode 100644 index 0000000..989c027 --- /dev/null +++ b/sicp/1_002e33 @@ -0,0 +1,45 @@ + +Exercise 1.33: You can obtain an even more +general version of accumulate (Exercise 1.32) by introducing the +notion of a +filter on the terms to be combined. That is, combine +only those terms derived from values in the range that satisfy a specified +condition. The resulting filtered-accumulate abstraction takes the same +arguments as accumulate, together with an additional predicate of one argument +that specifies the filter. Write filtered-accumulate as a procedure. +Show how to express the following using filtered-accumulate: + + + the sum of the squares of the prime numbers in the interval + a + to + b + +(assuming that you have a prime? predicate already written) + + the product of all the positive integers less than + n + that are relatively +prime to + n + (i.e., all positive integers + + i + < + n + + such that + + + GCD + ( + i + , + n + ) + = + 1 + +). + + diff --git a/sicp/1_002e34 b/sicp/1_002e34 new file mode 100644 index 0000000..6a7f0b1 --- /dev/null +++ b/sicp/1_002e34 @@ -0,0 +1,18 @@ + +Exercise 1.34: Suppose we define the procedure + + +(define (f g) (g 2)) + +Then we have + + +(f square) +4 + +(f (lambda (z) (* z (+ z 1)))) +6 + + +What happens if we (perversely) ask the interpreter to evaluate the combination +(f f)? Explain. diff --git a/sicp/1_002e35 b/sicp/1_002e35 new file mode 100644 index 0000000..3f61b4f --- /dev/null +++ b/sicp/1_002e35 @@ -0,0 +1,21 @@ + +Exercise 1.35: Show that the golden ratio + + φ + (1.2.2) is a fixed point of the transformation + + + x + ↦ + 1 + + + 1 + + / + + x + +, and use this fact to compute + φ + by means +of the fixed-point procedure. diff --git a/sicp/1_002e36 b/sicp/1_002e36 new file mode 100644 index 0000000..c59e6ac --- /dev/null +++ b/sicp/1_002e36 @@ -0,0 +1,46 @@ + +Exercise 1.36: Modify fixed-point so that +it prints the sequence of approximations it generates, using the newline +and display primitives shown in Exercise 1.22. Then find a +solution to + + + x + x + + = + 1000 + + by finding a fixed point of + x + ↦ + + log + ⁡ + ( + 1000 + ) + + / + + log + ⁡ + ( + x + ) + +. (Use Scheme’s primitive log +procedure, which computes natural logarithms.) Compare the number of steps +this takes with and without average damping. (Note that you cannot start +fixed-point with a guess of 1, as this would cause division by + + + log + ⁡ + ( + 1 + ) + = + 0 + +.) diff --git a/sicp/1_002e37 b/sicp/1_002e37 new file mode 100644 index 0000000..19987d1 --- /dev/null +++ b/sicp/1_002e37 @@ -0,0 +1,166 @@ + +Exercise 1.37: + + + An infinite +continued fraction is an expression of the form + + + f + + = + + + + + N + 1 + + + + D + 1 + + + + + + N + 2 + + + + D + 2 + + + + + + N + 3 + + + + D + 3 + + + + … + + + + + + + . + + + +As an example, one can show that the infinite continued fraction expansion with +the + + N + i + + and the + + D + i + + all equal to 1 produces + + 1 + + / + + φ + +, where + + φ + is the golden ratio (described in 1.2.2). One way to +approximate an infinite continued fraction is to truncate the expansion after a +given number of terms. Such a truncation—a so-called +finite continued fraction +k-term +finite continued fraction—has the form + + + + + + N + 1 + + + + D + 1 + + + + + + N + 2 + + + ⋱ + + + + + N + k + + + D + k + + + + + + + . + + + +Suppose that n and d are procedures of one argument (the term +index + i +) that return the + + N + i + + and + + D + i + + of the terms of the +continued fraction. Define a procedure cont-frac such that evaluating +(cont-frac n d k) computes the value of the + k +-term finite continued +fraction. Check your procedure by approximating + + 1 + + / + + φ + + using + + +(cont-frac (lambda (i) 1.0) + (lambda (i) 1.0) + k) + +for successive values of k. How large must you make k in order +to get an approximation that is accurate to 4 decimal places? + + If your cont-frac procedure generates a recursive process, write one +that generates an iterative process. If it generates an iterative process, +write one that generates a recursive process. + + diff --git a/sicp/1_002e38 b/sicp/1_002e38 new file mode 100644 index 0000000..734eeff --- /dev/null +++ b/sicp/1_002e38 @@ -0,0 +1,28 @@ + +Exercise 1.38: In 1737, the Swiss mathematician +Leonhard Euler published a memoir De Fractionibus Continuis, which +included a continued fraction expansion for + + e + − + 2 + +, where + e + is the base +of the natural logarithms. In this fraction, the + + N + i + + are all 1, and +the + + D + i + + are successively 1, 2, 1, 1, 4, 1, 1, 6, 1, 1, 8, …. +Write a program that uses your cont-frac procedure from Exercise 1.37 +to approximate + e +, based on Euler’s expansion. diff --git a/sicp/1_002e39 b/sicp/1_002e39 new file mode 100644 index 0000000..3f709ef --- /dev/null +++ b/sicp/1_002e39 @@ -0,0 +1,53 @@ + +Exercise 1.39: A continued fraction +representation of the tangent function was published in 1770 by the German +mathematician J.H. Lambert: + + + + tan + ⁡ + x + + + = + + + + x + + 1 + − + + + x + 2 + + + 3 + − + + + x + 2 + + + 5 + − + … + + + + + + + + , + + + +where + x + is in radians. Define a procedure (tan-cf x k) that +computes an approximation to the tangent function based on Lambert’s formula. +k specifies the number of terms to compute, as in Exercise 1.37. diff --git a/sicp/1_002e4 b/sicp/1_002e4 new file mode 100644 index 0000000..e3efecf --- /dev/null +++ b/sicp/1_002e4 @@ -0,0 +1,8 @@ + +Exercise 1.4: Observe that our model of +evaluation allows for combinations whose operators are compound expressions. +Use this observation to describe the behavior of the following procedure: + + +(define (a-plus-abs-b a b) + ((if (> b 0) + -) a b)) diff --git a/sicp/1_002e40 b/sicp/1_002e40 new file mode 100644 index 0000000..8c8861b --- /dev/null +++ b/sicp/1_002e40 @@ -0,0 +1,27 @@ + +Exercise 1.40: Define a procedure cubic +that can be used together with the newtons-method procedure in +expressions of the form + + +(newtons-method (cubic a b c) 1) + +to approximate zeros of the cubic + + + x + 3 + + + + a + + x + 2 + + + + b + x + + + c + +. diff --git a/sicp/1_002e41 b/sicp/1_002e41 new file mode 100644 index 0000000..6febead --- /dev/null +++ b/sicp/1_002e41 @@ -0,0 +1,9 @@ + +Exercise 1.41: Define a procedure double +that takes a procedure of one argument as argument and returns a procedure that +applies the original procedure twice. For example, if inc is a +procedure that adds 1 to its argument, then (double inc) should be a +procedure that adds 2. What value is returned by + + +(((double (double double)) inc) 5) diff --git a/sicp/1_002e42 b/sicp/1_002e42 new file mode 100644 index 0000000..fcc3a34 --- /dev/null +++ b/sicp/1_002e42 @@ -0,0 +1,32 @@ + +Exercise 1.42: Let + f + and + g + be two +one-argument functions. The +composition + f + after + g + is defined +to be the function + + x + ↦ + f + ( + g + ( + x + ) + ) + +. Define a procedure +compose that implements composition. For example, if inc is a +procedure that adds 1 to its argument, + + +((compose square inc) 6) +49 + diff --git a/sicp/1_002e43 b/sicp/1_002e43 new file mode 100644 index 0000000..c826ea9 --- /dev/null +++ b/sicp/1_002e43 @@ -0,0 +1,110 @@ + +Exercise 1.43: If + f + is a numerical function +and + n + is a positive integer, then we can form the + + n + + th + + + repeated +application of + f +, which is defined to be the function whose value at + x + +is + + f + ( + f + ( + … + ( + f + ( + x + ) + ) + … + ) + ) + +. For example, if + f + is the +function + + x + ↦ + x + + + 1 + +, then the + + n + + th + + + repeated application of + f + is +the function + + x + ↦ + x + + + n + +. If + f + is the operation of squaring a +number, then the + + n + + th + + + repeated application of + f + is the function that +raises its argument to the + + + 2 + n + + -th + + power. Write a procedure that takes as +inputs a procedure that computes + f + and a positive integer + n + and returns +the procedure that computes the + + n + + th + + + repeated application of + f +. Your +procedure should be able to be used as follows: + + +((repeated square 2) 5) +625 + + +Hint: You may find it convenient to use compose from Exercise 1.42. diff --git a/sicp/1_002e44 b/sicp/1_002e44 new file mode 100644 index 0000000..d358bdf --- /dev/null +++ b/sicp/1_002e44 @@ -0,0 +1,56 @@ + +Exercise 1.44: The idea of +smoothing a +function is an important concept in signal processing. If + f + is a function +and + + d + x + + is some small number, then the smoothed version of + f + is the +function whose value at a point + x + is the average of + + f + ( + x + − + d + x + ) + +, + + + f + ( + x + ) + +, and + + f + ( + x + + + d + x + ) + +. Write a procedure +smooth that takes as input a procedure that computes + f + and returns a +procedure that computes the smoothed + f +. It is sometimes valuable to +repeatedly smooth a function (that is, smooth the smoothed function, and so on) +to obtain the +n-fold smoothed function. Show how to generate +the n-fold smoothed function of any given function using smooth and +repeated from Exercise 1.43. diff --git a/sicp/1_002e45 b/sicp/1_002e45 new file mode 100644 index 0000000..fe39184 --- /dev/null +++ b/sicp/1_002e45 @@ -0,0 +1,97 @@ + +Exercise 1.45: We saw in 1.3.3 +that attempting to compute square roots by naively finding a fixed point of + + + y + ↦ + x + + / + + y + + does not converge, and that this can be fixed by average +damping. The same method works for finding cube roots as fixed points of the +average-damped + + y + ↦ + x + + / + + + y + 2 + + +. Unfortunately, the process does not +work for fourth roots—a single average damp is not enough to make a +fixed-point search for + + y + ↦ + x + + / + + + y + 3 + + + converge. On the other hand, if +we average damp twice (i.e., use the average damp of the average damp of + + + y + ↦ + x + + / + + + y + 3 + + +) the fixed-point search does converge. Do some experiments +to determine how many average damps are required to compute + + n + + th + + + roots as a +fixed-point search based upon repeated average damping of + + y + ↦ + x + + / + + + y + + + n + − + 1 + + + +. +Use this to implement a simple procedure for computing + + + n + + th + + + roots using fixed-point, average-damp, and the +repeated procedure of Exercise 1.43. Assume that any arithmetic +operations you need are available as primitives. diff --git a/sicp/1_002e46 b/sicp/1_002e46 new file mode 100644 index 0000000..22af21d --- /dev/null +++ b/sicp/1_002e46 @@ -0,0 +1,15 @@ + +Exercise 1.46: Several of the numerical methods +described in this chapter are instances of an extremely general computational +strategy known as +iterative improvement. Iterative improvement says +that, to compute something, we start with an initial guess for the answer, test +if the guess is good enough, and otherwise improve the guess and continue the +process using the improved guess as the new guess. Write a procedure +iterative-improve that takes two procedures as arguments: a method for +telling whether a guess is good enough and a method for improving a guess. +Iterative-improve should return as its value a procedure that takes a +guess as argument and keeps improving the guess until it is good enough. +Rewrite the sqrt procedure of 1.1.7 and the +fixed-point procedure of 1.3.3 in terms of +iterative-improve. diff --git a/sicp/1_002e5 b/sicp/1_002e5 new file mode 100644 index 0000000..1472cc1 --- /dev/null +++ b/sicp/1_002e5 @@ -0,0 +1,26 @@ + +Exercise 1.5: Ben Bitdiddle has invented a test +to determine whether the interpreter he is faced with is using +applicative-order evaluation or normal-order evaluation. He defines the +following two procedures: + + +(define (p) (p)) + +(define (test x y) + (if (= x 0) + 0 + y)) + +Then he evaluates the expression + + +(test 0 (p)) + +What behavior will Ben observe with an interpreter that uses applicative-order +evaluation? What behavior will he observe with an interpreter that uses +normal-order evaluation? Explain your answer. (Assume that the evaluation +rule for the special form if is the same whether the interpreter is +using normal or applicative order: The predicate expression is evaluated first, +and the result determines whether to evaluate the consequent or the alternative +expression.) diff --git a/sicp/1_002e6 b/sicp/1_002e6 new file mode 100644 index 0000000..ab6f7b3 --- /dev/null +++ b/sicp/1_002e6 @@ -0,0 +1,34 @@ + +Exercise 1.6: Alyssa P. Hacker doesn’t see why +if needs to be provided as a special form. “Why can’t I just define it +as an ordinary procedure in terms of cond?” she asks. Alyssa’s friend +Eva Lu Ator claims this can indeed be done, and she defines a new version of +if: + + +(define (new-if predicate + then-clause + else-clause) + (cond (predicate then-clause) + (else else-clause))) + +Eva demonstrates the program for Alyssa: + + +(new-if (= 2 3) 0 5) +5 + +(new-if (= 1 1) 0 5) +0 + + +Delighted, Alyssa uses new-if to rewrite the square-root program: + + +(define (sqrt-iter guess x) + (new-if (good-enough? guess x) + guess + (sqrt-iter (improve guess x) x))) + +What happens when Alyssa attempts to use this to compute square roots? +Explain. diff --git a/sicp/1_002e7 b/sicp/1_002e7 new file mode 100644 index 0000000..53fda9d --- /dev/null +++ b/sicp/1_002e7 @@ -0,0 +1,11 @@ + +Exercise 1.7: The good-enough? test used +in computing square roots will not be very effective for finding the square +roots of very small numbers. Also, in real computers, arithmetic operations +are almost always performed with limited precision. This makes our test +inadequate for very large numbers. Explain these statements, with examples +showing how the test fails for small and large numbers. An alternative +strategy for implementing good-enough? is to watch how guess +changes from one iteration to the next and to stop when the change is a very +small fraction of the guess. Design a square-root procedure that uses this +kind of end test. Does this work better for small and large numbers? diff --git a/sicp/1_002e8 b/sicp/1_002e8 new file mode 100644 index 0000000..9f82cd2 --- /dev/null +++ b/sicp/1_002e8 @@ -0,0 +1,37 @@ + +Exercise 1.8: Newton’s method for cube roots is +based on the fact that if + y + is an approximation to the cube root of + x +, +then a better approximation is given by the value + + + + + + + x + + / + + + y + 2 + + + + + 2 + y + + 3 + + . + + + +Use this formula to implement a cube-root procedure analogous to the +square-root procedure. (In 1.3.4 we will see how to implement +Newton’s method in general as an abstraction of these square-root and cube-root +procedures.) diff --git a/sicp/1_002e9 b/sicp/1_002e9 new file mode 100644 index 0000000..b92a8e3 --- /dev/null +++ b/sicp/1_002e9 @@ -0,0 +1,20 @@ + +Exercise 1.9: Each of the following two +procedures defines a method for adding two positive integers in terms of the +procedures inc, which increments its argument by 1, and dec, +which decrements its argument by 1. + + +(define (+ a b) + (if (= a 0) + b + (inc (+ (dec a) b)))) + +(define (+ a b) + (if (= a 0) + b + (+ (dec a) (inc b)))) + +Using the substitution model, illustrate the process generated by each +procedure in evaluating (+ 4 5). Are these processes iterative or +recursive? diff --git a/sicp/2_002e1 b/sicp/2_002e1 new file mode 100644 index 0000000..a2bf274 --- /dev/null +++ b/sicp/2_002e1 @@ -0,0 +1,6 @@ + +Exercise 2.1: Define a better version of +make-rat that handles both positive and negative arguments. +Make-rat should normalize the sign so that if the rational number is +positive, both the numerator and denominator are positive, and if the rational +number is negative, only the numerator is negative. diff --git a/sicp/2_002e10 b/sicp/2_002e10 new file mode 100644 index 0000000..8e2aec2 --- /dev/null +++ b/sicp/2_002e10 @@ -0,0 +1,5 @@ + +Exercise 2.10: Ben Bitdiddle, an expert systems +programmer, looks over Alyssa’s shoulder and comments that it is not clear what +it means to divide by an interval that spans zero. Modify Alyssa’s code to +check for this condition and to signal an error if it occurs. diff --git a/sicp/2_002e11 b/sicp/2_002e11 new file mode 100644 index 0000000..2ac33e4 --- /dev/null +++ b/sicp/2_002e11 @@ -0,0 +1,35 @@ + +Exercise 2.11: In passing, Ben also cryptically +comments: “By testing the signs of the endpoints of the intervals, it is +possible to break mul-interval into nine cases, only one of which +requires more than two multiplications.” Rewrite this procedure using Ben’s +suggestion. + +After debugging her program, Alyssa shows it to a potential user, who complains +that her program solves the wrong problem. He wants a program that can deal +with numbers represented as a center value and an additive tolerance; for +example, he wants to work with intervals such as 3.5 + ± + 0.15 rather than +[3.35, 3.65]. Alyssa returns to her desk and fixes this problem by supplying +an alternate constructor and alternate selectors: + + +(define (make-center-width c w) + (make-interval (- c w) (+ c w))) + +(define (center i) + (/ (+ (lower-bound i) + (upper-bound i)) + 2)) + +(define (width i) + (/ (- (upper-bound i) + (lower-bound i)) + 2)) + +Unfortunately, most of Alyssa’s users are engineers. Real engineering +situations usually involve measurements with only a small uncertainty, measured +as the ratio of the width of the interval to the midpoint of the interval. +Engineers usually specify percentage tolerances on the parameters of devices, +as in the resistor specifications given earlier. diff --git a/sicp/2_002e12 b/sicp/2_002e12 new file mode 100644 index 0000000..18e629d --- /dev/null +++ b/sicp/2_002e12 @@ -0,0 +1,6 @@ + +Exercise 2.12: Define a constructor +make-center-percent that takes a center and a percentage tolerance and +produces the desired interval. You must also define a selector percent +that produces the percentage tolerance for a given interval. The center +selector is the same as the one shown above. diff --git a/sicp/2_002e13 b/sicp/2_002e13 new file mode 100644 index 0000000..8de55ee --- /dev/null +++ b/sicp/2_002e13 @@ -0,0 +1,88 @@ + +Exercise 2.13: Show that under the assumption of +small percentage tolerances there is a simple formula for the approximate +percentage tolerance of the product of two intervals in terms of the tolerances +of the factors. You may simplify the problem by assuming that all numbers are +positive. + +After considerable work, Alyssa P. Hacker delivers her finished system. +Several years later, after she has forgotten all about it, she gets a frenzied +call from an irate user, Lem E. Tweakit. It seems that Lem has noticed that +the formula for parallel resistors can be written in two algebraically +equivalent ways: + + + + + + R + 1 + + + R + 2 + + + + + R + 1 + + + + + R + 2 + + + + + +and + + + + + 1 + + 1 + + / + + + R + 1 + + + + 1 + + / + + + R + 2 + + + + + . + + +He has written the following two programs, each of which computes the +parallel-resistors formula differently: + + +(define (par1 r1 r2) + (div-interval + (mul-interval r1 r2) + (add-interval r1 r2))) + +(define (par2 r1 r2) + (let ((one (make-interval 1 1))) + (div-interval + one + (add-interval + (div-interval one r1) + (div-interval one r2))))) + +Lem complains that Alyssa’s program gives different answers for the two ways of +computing. This is a serious complaint. diff --git a/sicp/2_002e14 b/sicp/2_002e14 new file mode 100644 index 0000000..bff937f --- /dev/null +++ b/sicp/2_002e14 @@ -0,0 +1,27 @@ + +Exercise 2.14: Demonstrate that Lem is right. +Investigate the behavior of the system on a variety of arithmetic +expressions. Make some intervals + A + and + B +, and use them in computing the +expressions + + A + + / + + A + + and + + A + + / + + B + +. You will get the most insight by +using intervals whose width is a small percentage of the center value. Examine +the results of the computation in center-percent form (see Exercise 2.12). diff --git a/sicp/2_002e15 b/sicp/2_002e15 new file mode 100644 index 0000000..c26bc87 --- /dev/null +++ b/sicp/2_002e15 @@ -0,0 +1,8 @@ + +Exercise 2.15: Eva Lu Ator, another user, has +also noticed the different intervals computed by different but algebraically +equivalent expressions. She says that a formula to compute with intervals using +Alyssa’s system will produce tighter error bounds if it can be written in such +a form that no variable that represents an uncertain number is repeated. Thus, +she says, par2 is a “better” program for parallel resistances than +par1. Is she right? Why? diff --git a/sicp/2_002e16 b/sicp/2_002e16 new file mode 100644 index 0000000..2315b2f --- /dev/null +++ b/sicp/2_002e16 @@ -0,0 +1,5 @@ + +Exercise 2.16: Explain, in general, why +equivalent algebraic expressions may lead to different answers. Can you devise +an interval-arithmetic package that does not have this shortcoming, or is this +task impossible? (Warning: This problem is very difficult.) diff --git a/sicp/2_002e17 b/sicp/2_002e17 new file mode 100644 index 0000000..cc34645 --- /dev/null +++ b/sicp/2_002e17 @@ -0,0 +1,9 @@ + +Exercise 2.17: Define a procedure +last-pair that returns the list that contains only the last element of a +given (nonempty) list: + + +(last-pair (list 23 72 149 34)) +(34) + diff --git a/sicp/2_002e18 b/sicp/2_002e18 new file mode 100644 index 0000000..20ab7d7 --- /dev/null +++ b/sicp/2_002e18 @@ -0,0 +1,9 @@ + +Exercise 2.18: Define a procedure reverse +that takes a list as argument and returns a list of the same elements in +reverse order: + + +(reverse (list 1 4 9 16 25)) +(25 16 9 4 1) + diff --git a/sicp/2_002e19 b/sicp/2_002e19 new file mode 100644 index 0000000..898dc38 --- /dev/null +++ b/sicp/2_002e19 @@ -0,0 +1,54 @@ + +Exercise 2.19: Consider the change-counting +program of 1.2.2. It would be nice to be able to easily change +the currency used by the program, so that we could compute the number of ways +to change a British pound, for example. As the program is written, the +knowledge of the currency is distributed partly into the procedure +first-denomination and partly into the procedure count-change +(which knows that there are five kinds of U.S. coins). It would be nicer to be +able to supply a list of coins to be used for making change. + +We want to rewrite the procedure cc so that its second argument is a +list of the values of the coins to use rather than an integer specifying which +coins to use. We could then have lists that defined each kind of currency: + + +(define us-coins + (list 50 25 10 5 1)) + +(define uk-coins + (list 100 50 20 10 5 2 1 0.5)) + +We could then call cc as follows: + + +(cc 100 us-coins) +292 + + +To do this will require changing the program cc somewhat. It will still +have the same form, but it will access its second argument differently, as +follows: + + +(define (cc amount coin-values) + (cond ((= amount 0) + 1) + ((or (< amount 0) + (no-more? coin-values)) + 0) + (else + (+ (cc + amount + (except-first-denomination + coin-values)) + (cc + (- amount + (first-denomination + coin-values)) + coin-values))))) + +Define the procedures first-denomination, +except-first-denomination and no-more? in terms of primitive +operations on list structures. Does the order of the list coin-values +affect the answer produced by cc? Why or why not? diff --git a/sicp/2_002e2 b/sicp/2_002e2 new file mode 100644 index 0000000..b291ccf --- /dev/null +++ b/sicp/2_002e2 @@ -0,0 +1,26 @@ + +Exercise 2.2: Consider the problem of +representing line segments in a plane. Each segment is represented as a pair +of points: a starting point and an ending point. Define a constructor +make-segment and selectors start-segment and end-segment +that define the representation of segments in terms of points. Furthermore, a +point can be represented as a pair of numbers: the + x + coordinate and the + + y + coordinate. Accordingly, specify a constructor make-point and +selectors x-point and y-point that define this representation. +Finally, using your selectors and constructors, define a procedure +midpoint-segment that takes a line segment as argument and returns its +midpoint (the point whose coordinates are the average of the coordinates of the +endpoints). To try your procedures, you’ll need a way to print points: + + +(define (print-point p) + (newline) + (display "(") + (display (x-point p)) + (display ",") + (display (y-point p)) + (display ")")) diff --git a/sicp/2_002e20 b/sicp/2_002e20 new file mode 100644 index 0000000..e934d53 --- /dev/null +++ b/sicp/2_002e20 @@ -0,0 +1,47 @@ + +Exercise 2.20: The procedures +, +*, and list take arbitrary numbers of arguments. One way to +define such procedures is to use define with +dotted-tail notation. +In a procedure definition, a parameter list that has a dot before +the last parameter name indicates that, when the procedure is called, the +initial parameters (if any) will have as values the initial arguments, as +usual, but the final parameter’s value will be a +list of any +remaining arguments. For instance, given the definition + + +(define (f x y . z) ⟨body⟩) + +the procedure f can be called with two or more arguments. If we +evaluate + + +(f 1 2 3 4 5 6) + +then in the body of f, x will be 1, y will be 2, and +z will be the list (3 4 5 6). Given the definition + + +(define (g . w) ⟨body⟩) + +the procedure g can be called with zero or more arguments. If we +evaluate + + +(g 1 2 3 4 5 6) + +then in the body of g, w will be the list (1 2 3 4 5 +6).77 + +Use this notation to write a procedure same-parity that takes one or +more integers and returns a list of all the arguments that have the same +even-odd parity as the first argument. For example, + + +(same-parity 1 2 3 4 5 6 7) +(1 3 5 7) + +(same-parity 2 3 4 5 6 7) +(2 4 6) + diff --git a/sicp/2_002e21 b/sicp/2_002e21 new file mode 100644 index 0000000..4a700e1 --- /dev/null +++ b/sicp/2_002e21 @@ -0,0 +1,21 @@ + +Exercise 2.21: The procedure square-list +takes a list of numbers as argument and returns a list of the squares of those +numbers. + + +(square-list (list 1 2 3 4)) +(1 4 9 16) + + +Here are two different definitions of square-list. Complete both of +them by filling in the missing expressions: + + +(define (square-list items) + (if (null? items) + nil + (cons ⟨??⟩ ⟨??⟩))) + +(define (square-list items) + (map ⟨??⟩ ⟨??⟩)) diff --git a/sicp/2_002e22 b/sicp/2_002e22 new file mode 100644 index 0000000..b6e0e3f --- /dev/null +++ b/sicp/2_002e22 @@ -0,0 +1,32 @@ + +Exercise 2.22: Louis Reasoner tries to rewrite +the first square-list procedure of Exercise 2.21 so that it +evolves an iterative process: + + +(define (square-list items) + (define (iter things answer) + (if (null? things) + answer + (iter (cdr things) + (cons (square (car things)) + answer)))) + (iter items nil)) + +Unfortunately, defining square-list this way produces the answer list in +the reverse order of the one desired. Why? + +Louis then tries to fix his bug by interchanging the arguments to cons: + + +(define (square-list items) + (define (iter things answer) + (if (null? things) + answer + (iter (cdr things) + (cons answer + (square + (car things)))))) + (iter items nil)) + +This doesn’t work either. Explain. diff --git a/sicp/2_002e23 b/sicp/2_002e23 new file mode 100644 index 0000000..6b80fbb --- /dev/null +++ b/sicp/2_002e23 @@ -0,0 +1,22 @@ + +Exercise 2.23: The procedure for-each is +similar to map. It takes as arguments a procedure and a list of +elements. However, rather than forming a list of the results, for-each +just applies the procedure to each of the elements in turn, from left to right. +The values returned by applying the procedure to the elements are not used at +all—for-each is used with procedures that perform an action, such as +printing. For example, + + +(for-each + (lambda (x) (newline) (display x)) + (list 57 321 88)) + +57 +321 +88 + + +The value returned by the call to for-each (not illustrated above) can +be something arbitrary, such as true. Give an implementation of +for-each. diff --git a/sicp/2_002e24 b/sicp/2_002e24 new file mode 100644 index 0000000..6405352 --- /dev/null +++ b/sicp/2_002e24 @@ -0,0 +1,5 @@ + +Exercise 2.24: Suppose we evaluate the +expression (list 1 (list 2 (list 3 4))). Give the result printed by the +interpreter, the corresponding box-and-pointer structure, and the +interpretation of this as a tree (as in Figure 2.6). diff --git a/sicp/2_002e25 b/sicp/2_002e25 new file mode 100644 index 0000000..e8b4789 --- /dev/null +++ b/sicp/2_002e25 @@ -0,0 +1,8 @@ + +Exercise 2.25: Give combinations of cars +and cdrs that will pick 7 from each of the following lists: + + +(1 3 (5 7) 9) +((7)) +(1 (2 (3 (4 (5 (6 7)))))) diff --git a/sicp/2_002e26 b/sicp/2_002e26 new file mode 100644 index 0000000..3f66673 --- /dev/null +++ b/sicp/2_002e26 @@ -0,0 +1,15 @@ + +Exercise 2.26: Suppose we define x and +y to be two lists: + + +(define x (list 1 2 3)) +(define y (list 4 5 6)) + +What result is printed by the interpreter in response to evaluating each of the +following expressions: + + +(append x y) +(cons x y) +(list x y) diff --git a/sicp/2_002e27 b/sicp/2_002e27 new file mode 100644 index 0000000..b4a63e6 --- /dev/null +++ b/sicp/2_002e27 @@ -0,0 +1,19 @@ + +Exercise 2.27: Modify your reverse +procedure of Exercise 2.18 to produce a deep-reverse procedure +that takes a list as argument and returns as its value the list with its +elements reversed and with all sublists deep-reversed as well. For example, + + +(define x + (list (list 1 2) (list 3 4))) + +x +((1 2) (3 4)) + +(reverse x) +((3 4) (1 2)) + +(deep-reverse x) +((4 3) (2 1)) + diff --git a/sicp/2_002e28 b/sicp/2_002e28 new file mode 100644 index 0000000..c6b0f73 --- /dev/null +++ b/sicp/2_002e28 @@ -0,0 +1,16 @@ + +Exercise 2.28: Write a procedure fringe +that takes as argument a tree (represented as a list) and returns a list whose +elements are all the leaves of the tree arranged in left-to-right order. For +example, + + +(define x + (list (list 1 2) (list 3 4))) + +(fringe x) +(1 2 3 4) + +(fringe (list x x)) +(1 2 3 4 1 2 3 4) + diff --git a/sicp/2_002e29 b/sicp/2_002e29 new file mode 100644 index 0000000..3f4e776 --- /dev/null +++ b/sicp/2_002e29 @@ -0,0 +1,48 @@ + +Exercise 2.29: A binary mobile consists of two +branches, a left branch and a right branch. Each branch is a rod of a certain +length, from which hangs either a weight or another binary mobile. We can +represent a binary mobile using compound data by constructing it from two +branches (for example, using list): + + +(define (make-mobile left right) + (list left right)) + +A branch is constructed from a length (which must be a number) together +with a structure, which may be either a number (representing a simple +weight) or another mobile: + + +(define (make-branch length structure) + (list length structure)) + + + Write the corresponding selectors left-branch and right-branch, +which return the branches of a mobile, and branch-length and +branch-structure, which return the components of a branch. + + Using your selectors, define a procedure total-weight that returns the +total weight of a mobile. + + A mobile is said to be +balanced if the torque applied by its top-left +branch is equal to that applied by its top-right branch (that is, if the length +of the left rod multiplied by the weight hanging from that rod is equal to the +corresponding product for the right side) and if each of the submobiles hanging +off its branches is balanced. Design a predicate that tests whether a binary +mobile is balanced. + + Suppose we change the representation of mobiles so that the constructors are + + +(define (make-mobile left right) + (cons left right)) + +(define (make-branch length structure) + (cons length structure)) + +How much do you need to change your programs to convert to the new +representation? + + diff --git a/sicp/2_002e3 b/sicp/2_002e3 new file mode 100644 index 0000000..bba3be1 --- /dev/null +++ b/sicp/2_002e3 @@ -0,0 +1,8 @@ + +Exercise 2.3: Implement a representation for +rectangles in a plane. (Hint: You may want to make use of Exercise 2.2.) +In terms of your constructors and selectors, create procedures that compute the +perimeter and the area of a given rectangle. Now implement a different +representation for rectangles. Can you design your system with suitable +abstraction barriers, so that the same perimeter and area procedures will work +using either representation? diff --git a/sicp/2_002e30 b/sicp/2_002e30 new file mode 100644 index 0000000..84357a1 --- /dev/null +++ b/sicp/2_002e30 @@ -0,0 +1,15 @@ + +Exercise 2.30: Define a procedure +square-tree analogous to the square-list procedure of +Exercise 2.21. That is, square-tree should behave as follows: + + +(square-tree + (list 1 + (list 2 (list 3 4) 5) + (list 6 7))) +(1 (4 (9 16) 25) (36 49)) + + +Define square-tree both directly (i.e., without using any higher-order +procedures) and also by using map and recursion. diff --git a/sicp/2_002e31 b/sicp/2_002e31 new file mode 100644 index 0000000..4eacfca --- /dev/null +++ b/sicp/2_002e31 @@ -0,0 +1,8 @@ + +Exercise 2.31: Abstract your answer to +Exercise 2.30 to produce a procedure tree-map with the property +that square-tree could be defined as + + +(define (square-tree tree) + (tree-map square tree)) diff --git a/sicp/2_002e32 b/sicp/2_002e32 new file mode 100644 index 0000000..45efcd2 --- /dev/null +++ b/sicp/2_002e32 @@ -0,0 +1,14 @@ + +Exercise 2.32: We can represent a set as a list +of distinct elements, and we can represent the set of all subsets of the set as +a list of lists. For example, if the set is (1 2 3), then the set of +all subsets is (() (3) (2) (2 3) (1) (1 3) (1 2) (1 2 3)). Complete the +following definition of a procedure that generates the set of subsets of a set +and give a clear explanation of why it works: + + +(define (subsets s) + (if (null? s) + (list nil) + (let ((rest (subsets (cdr s)))) + (append rest (map ⟨??⟩ rest))))) diff --git a/sicp/2_002e33 b/sicp/2_002e33 new file mode 100644 index 0000000..77fe9cd --- /dev/null +++ b/sicp/2_002e33 @@ -0,0 +1,15 @@ + +Exercise 2.33: Fill in the missing expressions +to complete the following definitions of some basic list-manipulation +operations as accumulations: + + +(define (map p sequence) + (accumulate (lambda (x y) ⟨??⟩) + nil sequence)) + +(define (append seq1 seq2) + (accumulate cons ⟨??⟩ ⟨??⟩)) + +(define (length sequence) + (accumulate ⟨??⟩ 0 sequence)) diff --git a/sicp/2_002e34 b/sicp/2_002e34 new file mode 100644 index 0000000..5ea8929 --- /dev/null +++ b/sicp/2_002e34 @@ -0,0 +1,182 @@ + +Exercise 2.34: Evaluating a polynomial in + x + +at a given value of + x + can be formulated as an accumulation. We evaluate +the polynomial + + + + a + n + + + x + n + + + + + + + a + + n + − + 1 + + + + x + + n + − + 1 + + + + + + ⋯ + + + + + a + 1 + + x + + + + + a + 0 + + +using a well-known algorithm called +Horner’s rule, which structures +the computation as + + + ( + … + ( + + a + n + + x + + + + + + a + + n + − + 1 + + + ) + x + + + + ⋯ + + + + + a + 1 + + ) + x + + + + + + a + 0 + + . + + +In other words, we start with + + a + n + +, multiply by + x +, add + + + a + + n + − + 1 + + +, multiply by + x +, and so on, until we reach + + + a + 0 + +.82 + +Fill in the following template to produce a procedure that evaluates a +polynomial using Horner’s rule. Assume that the coefficients of the polynomial +are arranged in a sequence, from + + a + 0 + + through + + a + n + +. + + +(define + (horner-eval x coefficient-sequence) + (accumulate + (lambda (this-coeff higher-terms) + ⟨??⟩) + 0 + coefficient-sequence)) + +For example, to compute + + 1 + + + 3 + x + + + + + 5 + + x + 3 + + + + + x + 5 + + + at + + x + = + 2 + + you +would evaluate + + +(horner-eval 2 (list 1 3 0 5 0 1)) diff --git a/sicp/2_002e35 b/sicp/2_002e35 new file mode 100644 index 0000000..626860e --- /dev/null +++ b/sicp/2_002e35 @@ -0,0 +1,7 @@ + +Exercise 2.35: Redefine count-leaves from +2.2.2 as an accumulation: + + +(define (count-leaves t) + (accumulate ⟨??⟩ ⟨??⟩ (map ⟨??⟩ ⟨??⟩))) diff --git a/sicp/2_002e36 b/sicp/2_002e36 new file mode 100644 index 0000000..a7fc53c --- /dev/null +++ b/sicp/2_002e36 @@ -0,0 +1,18 @@ + +Exercise 2.36: The procedure accumulate-n +is similar to accumulate except that it takes as its third argument a +sequence of sequences, which are all assumed to have the same number of +elements. It applies the designated accumulation procedure to combine all the +first elements of the sequences, all the second elements of the sequences, and +so on, and returns a sequence of the results. For instance, if s is a +sequence containing four sequences, ((1 2 3) (4 5 6) (7 8 9) (10 11 +12)), then the value of (accumulate-n + 0 s) should be the sequence +(22 26 30). Fill in the missing expressions in the following definition +of accumulate-n: + + +(define (accumulate-n op init seqs) + (if (null? (car seqs)) + nil + (cons (accumulate op init ⟨??⟩) + (accumulate-n op init ⟨??⟩)))) diff --git a/sicp/2_002e37 b/sicp/2_002e37 new file mode 100644 index 0000000..b2e4fef --- /dev/null +++ b/sicp/2_002e37 @@ -0,0 +1,250 @@ + +Exercise 2.37: +Suppose we represent vectors v = + + ( + + v + i + + ) + + as sequences of numbers, and +matrices m = + + ( + + m + + i + j + + + ) + + as sequences of vectors (the rows of the +matrix). For example, the matrix + + + ( + + + + 1 + + + 2 + + + 3 + + + 4 + + + + + 4 + + + 5 + + + 6 + + + 6 + + + + + 6 + + + 7 + + + 8 + + + 9 + + + + ) + + +is represented as the sequence ((1 2 3 4) (4 5 6 6) (6 7 8 9)). With +this representation, we can use sequence operations to concisely express the +basic matrix and vector operations. These operations (which are described in +any book on matrix algebra) are the following: + + + + + (dot-product v w) + + + returns the sum + + + Σ + i + + + v + i + + + w + i + + ; + + + + + (matrix-*-vector m v) + + + returns the vector + + + t + + , + + + + + + where + + + t + i + + = + + Σ + j + + + m + + i + j + + + + v + j + + ; + + + + + (matrix-*-matrix m n) + + + returns the matrix + + + p + + , + + + + + + where + + + p + + i + j + + + = + + Σ + k + + + m + + i + k + + + + n + + k + j + + + ; + + + + + (transpose m) + + + returns the matrix + + + n + + , + + + + + + where + + + n + + i + j + + + = + + m + + j + i + + + . + + + + +We can define the dot product as83 + + +(define (dot-product v w) + (accumulate + 0 (map * v w))) + +Fill in the missing expressions in the following procedures for computing the +other matrix operations. (The procedure accumulate-n is defined in +Exercise 2.36.) + + +(define (matrix-*-vector m v) + (map ⟨??⟩ m)) + +(define (transpose mat) + (accumulate-n ⟨??⟩ ⟨??⟩ mat)) + +(define (matrix-*-matrix m n) + (let ((cols (transpose n))) + (map ⟨??⟩ m))) diff --git a/sicp/2_002e38 b/sicp/2_002e38 new file mode 100644 index 0000000..02a7b46 --- /dev/null +++ b/sicp/2_002e38 @@ -0,0 +1,27 @@ + +Exercise 2.38: The accumulate procedure +is also known as fold-right, because it combines the first element of +the sequence with the result of combining all the elements to the right. There +is also a fold-left, which is similar to fold-right, except that +it combines elements working in the opposite direction: + + +(define (fold-left op initial sequence) + (define (iter result rest) + (if (null? rest) + result + (iter (op result (car rest)) + (cdr rest)))) + (iter initial sequence)) + +What are the values of + + +(fold-right / 1 (list 1 2 3)) +(fold-left / 1 (list 1 2 3)) +(fold-right list nil (list 1 2 3)) +(fold-left list nil (list 1 2 3)) + +Give a property that op should satisfy to guarantee that +fold-right and fold-left will produce the same values for any +sequence. diff --git a/sicp/2_002e39 b/sicp/2_002e39 new file mode 100644 index 0000000..8b1efad --- /dev/null +++ b/sicp/2_002e39 @@ -0,0 +1,13 @@ + +Exercise 2.39: Complete the following +definitions of reverse (Exercise 2.18) in terms of +fold-right and fold-left from Exercise 2.38: + + +(define (reverse sequence) + (fold-right + (lambda (x y) ⟨??⟩) nil sequence)) + +(define (reverse sequence) + (fold-left + (lambda (x y) ⟨??⟩) nil sequence)) diff --git a/sicp/2_002e4 b/sicp/2_002e4 new file mode 100644 index 0000000..3d82d4b --- /dev/null +++ b/sicp/2_002e4 @@ -0,0 +1,14 @@ + +Exercise 2.4: Here is an alternative procedural +representation of pairs. For this representation, verify that (car (cons +x y)) yields x for any objects x and y. + + +(define (cons x y) + (lambda (m) (m x y))) + +(define (car z) + (z (lambda (p q) p))) + +What is the corresponding definition of cdr? (Hint: To verify that this +works, make use of the substitution model of 1.1.5.) diff --git a/sicp/2_002e40 b/sicp/2_002e40 new file mode 100644 index 0000000..37a7b24 --- /dev/null +++ b/sicp/2_002e40 @@ -0,0 +1,27 @@ + +Exercise 2.40: Define a procedure +unique-pairs that, given an integer + n +, generates the sequence of +pairs + + ( + i + , + j + ) + + with + + 1 + ≤ + j + + < + + i + ≤ + n + +. Use unique-pairs +to simplify the definition of prime-sum-pairs given above. diff --git a/sicp/2_002e41 b/sicp/2_002e41 new file mode 100644 index 0000000..62c24e2 --- /dev/null +++ b/sicp/2_002e41 @@ -0,0 +1,14 @@ + +Exercise 2.41: Write a procedure to find all +ordered triples of distinct positive integers + i +, + j +, and + k + less than +or equal to a given integer + n + that sum to a given integer + s +. diff --git a/sicp/2_002e42 b/sicp/2_002e42 new file mode 100644 index 0000000..584dc16 --- /dev/null +++ b/sicp/2_002e42 @@ -0,0 +1,142 @@ + +Exercise 2.42: The “eight-queens puzzle” asks +how to place eight queens on a chessboard so that no queen is in check from any +other (i.e., no two queens are in the same row, column, or diagonal). One +possible solution is shown in Figure 2.8. One way to solve the puzzle is +to work across the board, placing a queen in each column. Once we have placed + + + k + − + 1 + + queens, we must place the + + k + + th + + + queen in a position where it does +not check any of the queens already on the board. We can formulate this +approach recursively: Assume that we have already generated the sequence of all +possible ways to place + + k + − + 1 + + queens in the first + + k + − + 1 + + columns of the +board. For each of these ways, generate an extended set of positions by +placing a queen in each row of the + + k + + th + + + column. Now filter these, keeping +only the positions for which the queen in the + + k + + th + + + column is safe with +respect to the other queens. This produces the sequence of all ways to place + + k + queens in the first + k + columns. By continuing this process, we will +produce not only one solution, but all solutions to the puzzle. + + + +SVG + + +Figure 2.8: A solution to the eight-queens puzzle. + + + +We implement this solution as a procedure queens, which returns a +sequence of all solutions to the problem of placing + n + queens on an + + + n + × + n + + chessboard. Queens has an internal procedure +queen-cols that returns the sequence of all ways to place queens in the +first + k + columns of the board. + + +(define (queens board-size) + (define (queen-cols k) + (if (= k 0) + (list empty-board) + (filter + (lambda (positions) + (safe? k positions)) + (flatmap + (lambda (rest-of-queens) + (map (lambda (new-row) + (adjoin-position + new-row + k + rest-of-queens)) + (enumerate-interval + 1 + board-size))) + (queen-cols (- k 1)))))) + (queen-cols board-size)) + +In this procedure rest-of-queens is a way to place + + k + − + 1 + + queens in +the first + + k + − + 1 + + columns, and new-row is a proposed row in which to +place the queen for the + + k + + th + + + column. Complete the program by implementing +the representation for sets of board positions, including the procedure +adjoin-position, which adjoins a new row-column position to a set of +positions, and empty-board, which represents an empty set of positions. +You must also write the procedure safe?, which determines for a set of +positions, whether the queen in the + + k + + th + + + column is safe with respect to the +others. (Note that we need only check whether the new queen is safe—the +other queens are already guaranteed safe with respect to each other.) diff --git a/sicp/2_002e43 b/sicp/2_002e43 new file mode 100644 index 0000000..155caf9 --- /dev/null +++ b/sicp/2_002e43 @@ -0,0 +1,28 @@ + +Exercise 2.43: Louis Reasoner is having a +terrible time doing Exercise 2.42. His queens procedure seems to +work, but it runs extremely slowly. (Louis never does manage to wait long +enough for it to solve even the + + 6 + × + 6 + + case.) When Louis asks Eva Lu Ator for +help, she points out that he has interchanged the order of the nested mappings +in the flatmap, writing it as + + +(flatmap + (lambda (new-row) + (map (lambda (rest-of-queens) + (adjoin-position + new-row k rest-of-queens)) + (queen-cols (- k 1)))) + (enumerate-interval 1 board-size)) + +Explain why this interchange makes the program run slowly. Estimate how long +it will take Louis’s program to solve the eight-queens puzzle, assuming that +the program in Exercise 2.42 solves the puzzle in time + T +. diff --git a/sicp/2_002e44 b/sicp/2_002e44 new file mode 100644 index 0000000..bdb6ec8 --- /dev/null +++ b/sicp/2_002e44 @@ -0,0 +1,5 @@ + +Exercise 2.44: Define the procedure +up-split used by corner-split. It is similar to +right-split, except that it switches the roles of below and +beside. diff --git a/sicp/2_002e45 b/sicp/2_002e45 new file mode 100644 index 0000000..c423d0e --- /dev/null +++ b/sicp/2_002e45 @@ -0,0 +1,11 @@ + +Exercise 2.45: Right-split and +up-split can be expressed as instances of a general splitting operation. +Define a procedure split with the property that evaluating + + +(define right-split (split beside below)) +(define up-split (split below beside)) + +produces procedures right-split and up-split with the same +behaviors as the ones already defined. diff --git a/sicp/2_002e46 b/sicp/2_002e46 new file mode 100644 index 0000000..160b4ca --- /dev/null +++ b/sicp/2_002e46 @@ -0,0 +1,151 @@ + +Exercise 2.46: A two-dimensional vector + v + +running from the origin to a point can be represented as a pair consisting of +an + x +-coordinate and a + y +-coordinate. Implement a data abstraction for +vectors by giving a constructor make-vect and corresponding selectors +xcor-vect and ycor-vect. In terms of your selectors and +constructor, implement procedures add-vect, sub-vect, and +scale-vect that perform the operations vector addition, vector +subtraction, and multiplying a vector by a scalar: + + + + + ( + + x + 1 + + , + + y + 1 + + ) + + + ( + + x + 2 + + , + + y + 2 + + ) + + + = + + + ( + + x + 1 + + + + + x + 2 + + , + + y + 1 + + + + + y + 2 + + ) + , + + + + + ( + + x + 1 + + , + + y + 1 + + ) + − + ( + + x + 2 + + , + + y + 2 + + ) + + + = + + + ( + + x + 1 + + − + + x + 2 + + , + + y + 1 + + − + + y + 2 + + ) + , + + + + + s + ⋅ + ( + x + , + y + ) + + + = + + + ( + s + x + , + s + y + ) + . + + + + diff --git a/sicp/2_002e47 b/sicp/2_002e47 new file mode 100644 index 0000000..005c145 --- /dev/null +++ b/sicp/2_002e47 @@ -0,0 +1,13 @@ + +Exercise 2.47: Here are two possible +constructors for frames: + + +(define (make-frame origin edge1 edge2) + (list origin edge1 edge2)) + +(define (make-frame origin edge1 edge2) + (cons origin (cons edge1 edge2))) + +For each constructor supply the appropriate selectors to produce an +implementation for frames. diff --git a/sicp/2_002e48 b/sicp/2_002e48 new file mode 100644 index 0000000..3c4bdbc --- /dev/null +++ b/sicp/2_002e48 @@ -0,0 +1,7 @@ + +Exercise 2.48: A directed line segment in the +plane can be represented as a pair of vectors—the vector running from the +origin to the start-point of the segment, and the vector running from the +origin to the end-point of the segment. Use your vector representation from +Exercise 2.46 to define a representation for segments with a constructor +make-segment and selectors start-segment and end-segment. diff --git a/sicp/2_002e49 b/sicp/2_002e49 new file mode 100644 index 0000000..f58b80d --- /dev/null +++ b/sicp/2_002e49 @@ -0,0 +1,15 @@ + +Exercise 2.49: Use segments->painter +to define the following primitive painters: + + + The painter that draws the outline of the designated frame. + + The painter that draws an “X” by connecting opposite corners of the frame. + + The painter that draws a diamond shape by connecting the midpoints of the sides +of the frame. + + The wave painter. + + diff --git a/sicp/2_002e5 b/sicp/2_002e5 new file mode 100644 index 0000000..63c8710 --- /dev/null +++ b/sicp/2_002e5 @@ -0,0 +1,21 @@ + +Exercise 2.5: Show that we can represent pairs of +nonnegative integers using only numbers and arithmetic operations if we +represent the pair + a + and + b + as the integer that is the product + + + 2 + a + + + 3 + b + + +. +Give the corresponding definitions of the procedures cons, +car, and cdr. diff --git a/sicp/2_002e50 b/sicp/2_002e50 new file mode 100644 index 0000000..2de3b5d --- /dev/null +++ b/sicp/2_002e50 @@ -0,0 +1,4 @@ + +Exercise 2.50: Define the transformation +flip-horiz, which flips painters horizontally, and transformations that +rotate painters counterclockwise by 180 degrees and 270 degrees. diff --git a/sicp/2_002e51 b/sicp/2_002e51 new file mode 100644 index 0000000..171226c --- /dev/null +++ b/sicp/2_002e51 @@ -0,0 +1,8 @@ + +Exercise 2.51: Define the below operation +for painters. Below takes two painters as arguments. The resulting +painter, given a frame, draws with the first painter in the bottom of the frame +and with the second painter in the top. Define below in two different +ways—first by writing a procedure that is analogous to the beside +procedure given above, and again in terms of beside and suitable +rotation operations (from Exercise 2.50). diff --git a/sicp/2_002e52 b/sicp/2_002e52 new file mode 100644 index 0000000..0a546d8 --- /dev/null +++ b/sicp/2_002e52 @@ -0,0 +1,18 @@ + +Exercise 2.52: Make changes to the square limit +of wave shown in Figure 2.9 by working at each of the levels +described above. In particular: + + + Add some segments to the primitive wave painter of Exercise 2.49 +(to add a smile, for example). + + Change the pattern constructed by corner-split (for example, by using +only one copy of the up-split and right-split images instead of +two). + + Modify the version of square-limit that uses square-of-four so as +to assemble the corners in a different pattern. (For example, you might make +the big Mr. Rogers look outward from each corner of the square.) + + diff --git a/sicp/2_002e53 b/sicp/2_002e53 new file mode 100644 index 0000000..2ac5fd3 --- /dev/null +++ b/sicp/2_002e53 @@ -0,0 +1,12 @@ + +Exercise 2.53: What would the interpreter print +in response to evaluating each of the following expressions? + + +(list 'a 'b 'c) +(list (list 'george)) +(cdr '((x1 x2) (y1 y2))) +(cadr '((x1 x2) (y1 y2))) +(pair? (car '(a short list))) +(memq 'red '((red shoes) (blue socks))) +(memq 'red '(red shoes blue socks)) diff --git a/sicp/2_002e54 b/sicp/2_002e54 new file mode 100644 index 0000000..a71f54c --- /dev/null +++ b/sicp/2_002e54 @@ -0,0 +1,21 @@ + +Exercise 2.54: Two lists are said to be +equal? if they contain equal elements arranged in the same order. For +example, + + +(equal? '(this is a list) + '(this is a list)) + +is true, but + + +(equal? '(this is a list) + '(this (is a) list)) + +is false. To be more precise, we can define equal? recursively in +terms of the basic eq? equality of symbols by saying that a and +b are equal? if they are both symbols and the symbols are +eq?, or if they are both lists such that (car a) is equal? +to (car b) and (cdr a) is equal? to (cdr b). Using +this idea, implement equal? as a procedure.102 diff --git a/sicp/2_002e55 b/sicp/2_002e55 new file mode 100644 index 0000000..d200b51 --- /dev/null +++ b/sicp/2_002e55 @@ -0,0 +1,8 @@ + +Exercise 2.55: Eva Lu Ator types to the +interpreter the expression + + +(car ''abracadabra) + +To her surprise, the interpreter prints back quote. Explain. diff --git a/sicp/2_002e56 b/sicp/2_002e56 new file mode 100644 index 0000000..3e485e3 --- /dev/null +++ b/sicp/2_002e56 @@ -0,0 +1,59 @@ + +Exercise 2.56: Show how to extend the basic +differentiator to handle more kinds of expressions. For instance, implement +the differentiation rule + + + + + d + ( + + u + + + n + + + ) + + + d + x + + + + + = + + + n + + u + + + n + − + 1 + + + + + + + d + u + + + d + x + + + + + +by adding a new clause to the deriv program and defining appropriate +procedures exponentiation?, base, exponent, and +make-exponentiation. (You may use the symbol ** to denote +exponentiation.) Build in the rules that anything raised to the power 0 is 1 +and anything raised to the power 1 is the thing itself. diff --git a/sicp/2_002e57 b/sicp/2_002e57 new file mode 100644 index 0000000..6a003db --- /dev/null +++ b/sicp/2_002e57 @@ -0,0 +1,12 @@ + +Exercise 2.57: Extend the differentiation +program to handle sums and products of arbitrary numbers of (two or more) +terms. Then the last example above could be expressed as + + +(deriv '(* x y (+ x 3)) 'x) + +Try to do this by changing only the representation for sums and products, +without changing the deriv procedure at all. For example, the +addend of a sum would be the first term, and the augend would be +the sum of the rest of the terms. diff --git a/sicp/2_002e58 b/sicp/2_002e58 new file mode 100644 index 0000000..5f426dc --- /dev/null +++ b/sicp/2_002e58 @@ -0,0 +1,22 @@ + +Exercise 2.58: Suppose we want to modify the +differentiation program so that it works with ordinary mathematical notation, +in which + and * are infix rather than prefix operators. Since +the differentiation program is defined in terms of abstract data, we can modify +it to work with different representations of expressions solely by changing the +predicates, selectors, and constructors that define the representation of the +algebraic expressions on which the differentiator is to operate. + + + Show how to do this in order to differentiate algebraic expressions presented +in infix form, such as (x + (3 * (x + (y + 2)))). To simplify the task, +assume that + and * always take two arguments and that +expressions are fully parenthesized. + + The problem becomes substantially harder if we allow standard algebraic +notation, such as (x + 3 * (x + y + 2)), which drops unnecessary +parentheses and assumes that multiplication is done before addition. Can you +design appropriate predicates, selectors, and constructors for this notation +such that our derivative program still works? + + diff --git a/sicp/2_002e59 b/sicp/2_002e59 new file mode 100644 index 0000000..369049c --- /dev/null +++ b/sicp/2_002e59 @@ -0,0 +1,3 @@ + +Exercise 2.59: Implement the union-set +operation for the unordered-list representation of sets. diff --git a/sicp/2_002e6 b/sicp/2_002e6 new file mode 100644 index 0000000..4d98fa7 --- /dev/null +++ b/sicp/2_002e6 @@ -0,0 +1,21 @@ + +Exercise 2.6: In case representing pairs as +procedures wasn’t mind-boggling enough, consider that, in a language that can +manipulate procedures, we can get by without numbers (at least insofar as +nonnegative integers are concerned) by implementing 0 and the operation of +adding 1 as + + +(define zero (lambda (f) (lambda (x) x))) + +(define (add-1 n) + (lambda (f) (lambda (x) (f ((n f) x))))) + +This representation is known as +Church numerals, after its inventor, +Alonzo Church, the logician who invented the λ-calculus. + +Define one and two directly (not in terms of zero and +add-1). (Hint: Use substitution to evaluate (add-1 zero)). Give +a direct definition of the addition procedure + (not in terms of +repeated application of add-1). diff --git a/sicp/2_002e60 b/sicp/2_002e60 new file mode 100644 index 0000000..541cd8c --- /dev/null +++ b/sicp/2_002e60 @@ -0,0 +1,19 @@ + +Exercise 2.60: We specified that a set would be +represented as a list with no duplicates. Now suppose we allow duplicates. +For instance, the set + + { + 1 + , + 2 + , + 3 + } + + could be represented as the list (2 3 2 1 +3 2 2). Design procedures element-of-set?, adjoin-set, +union-set, and intersection-set that operate on this +representation. How does the efficiency of each compare with the corresponding +procedure for the non-duplicate representation? Are there applications for +which you would use this representation in preference to the non-duplicate one? diff --git a/sicp/2_002e61 b/sicp/2_002e61 new file mode 100644 index 0000000..7c4bab9 --- /dev/null +++ b/sicp/2_002e61 @@ -0,0 +1,6 @@ + +Exercise 2.61: Give an implementation of +adjoin-set using the ordered representation. By analogy with +element-of-set? show how to take advantage of the ordering to produce a +procedure that requires on the average about half as many steps as with the +unordered representation. diff --git a/sicp/2_002e62 b/sicp/2_002e62 new file mode 100644 index 0000000..2330223 --- /dev/null +++ b/sicp/2_002e62 @@ -0,0 +1,10 @@ + +Exercise 2.62: Give a + + Θ + ( + n + ) + + +implementation of union-set for sets represented as ordered lists. diff --git a/sicp/2_002e63 b/sicp/2_002e63 new file mode 100644 index 0000000..c416e8a --- /dev/null +++ b/sicp/2_002e63 @@ -0,0 +1,39 @@ + +Exercise 2.63: Each of the following two +procedures converts a binary tree to a list. + + +(define (tree->list-1 tree) + (if (null? tree) + '() + (append + (tree->list-1 + (left-branch tree)) + (cons (entry tree) + (tree->list-1 + (right-branch tree)))))) + +(define (tree->list-2 tree) + (define (copy-to-list tree result-list) + (if (null? tree) + result-list + (copy-to-list + (left-branch tree) + (cons (entry tree) + (copy-to-list + (right-branch tree) + result-list))))) + (copy-to-list tree '())) + + + Do the two procedures produce the same result for every tree? If not, how do +the results differ? What lists do the two procedures produce for the trees in +Figure 2.16? + + Do the two procedures have the same order of growth in the number of steps +required to convert a balanced tree with + n + elements to a list? If not, +which one grows more slowly? + + diff --git a/sicp/2_002e64 b/sicp/2_002e64 new file mode 100644 index 0000000..9248036 --- /dev/null +++ b/sicp/2_002e64 @@ -0,0 +1,60 @@ + +Exercise 2.64: The following procedure +list->tree converts an ordered list to a balanced binary tree. The +helper procedure partial-tree takes as arguments an integer + n + and +list of at least + n + elements and constructs a balanced tree containing the +first + n + elements of the list. The result returned by partial-tree +is a pair (formed with cons) whose car is the constructed tree +and whose cdr is the list of elements not included in the tree. + + +(define (list->tree elements) + (car (partial-tree + elements (length elements)))) + +(define (partial-tree elts n) + (if (= n 0) + (cons '() elts) + (let ((left-size + (quotient (- n 1) 2))) + (let ((left-result + (partial-tree + elts left-size))) + (let ((left-tree + (car left-result)) + (non-left-elts + (cdr left-result)) + (right-size + (- n (+ left-size 1)))) + (let ((this-entry + (car non-left-elts)) + (right-result + (partial-tree + (cdr non-left-elts) + right-size))) + (let ((right-tree + (car right-result)) + (remaining-elts + (cdr right-result))) + (cons (make-tree this-entry + left-tree + right-tree) + remaining-elts)))))))) + + + Write a short paragraph explaining as clearly as you can how +partial-tree works. Draw the tree produced by list->tree for +the list (1 3 5 7 9 11). + + What is the order of growth in the number of steps required by +list->tree to convert a list of + n + elements? + + diff --git a/sicp/2_002e65 b/sicp/2_002e65 new file mode 100644 index 0000000..8169619 --- /dev/null +++ b/sicp/2_002e65 @@ -0,0 +1,12 @@ + +Exercise 2.65: Use the results of Exercise 2.63 +and Exercise 2.64 to give + + Θ + ( + n + ) + + implementations of +union-set and intersection-set for sets implemented as (balanced) +binary trees.107 diff --git a/sicp/2_002e66 b/sicp/2_002e66 new file mode 100644 index 0000000..8c038c0 --- /dev/null +++ b/sicp/2_002e66 @@ -0,0 +1,4 @@ + +Exercise 2.66: Implement the lookup +procedure for the case where the set of records is structured as a binary tree, +ordered by the numerical values of the keys. diff --git a/sicp/2_002e67 b/sicp/2_002e67 new file mode 100644 index 0000000..e9970a2 --- /dev/null +++ b/sicp/2_002e67 @@ -0,0 +1,18 @@ + +Exercise 2.67: Define an encoding tree and a +sample message: + + +(define sample-tree + (make-code-tree + (make-leaf 'A 4) + (make-code-tree + (make-leaf 'B 2) + (make-code-tree + (make-leaf 'D 1) + (make-leaf 'C 1))))) + +(define sample-message + '(0 1 1 0 0 1 0 1 0 1 1 1 0)) + +Use the decode procedure to decode the message, and give the result. diff --git a/sicp/2_002e68 b/sicp/2_002e68 new file mode 100644 index 0000000..d72c3d8 --- /dev/null +++ b/sicp/2_002e68 @@ -0,0 +1,20 @@ + +Exercise 2.68: The encode procedure takes +as arguments a message and a tree and produces the list of bits that gives the +encoded message. + + +(define (encode message tree) + (if (null? message) + '() + (append + (encode-symbol (car message) + tree) + (encode (cdr message) tree)))) + +Encode-symbol is a procedure, which you must write, that returns the +list of bits that encodes a given symbol according to a given tree. You should +design encode-symbol so that it signals an error if the symbol is not in +the tree at all. Test your procedure by encoding the result you obtained in +Exercise 2.67 with the sample tree and seeing whether it is the same as +the original sample message. diff --git a/sicp/2_002e69 b/sicp/2_002e69 new file mode 100644 index 0000000..34ea053 --- /dev/null +++ b/sicp/2_002e69 @@ -0,0 +1,19 @@ + +Exercise 2.69: The following procedure takes as +its argument a list of symbol-frequency pairs (where no symbol appears in more +than one pair) and generates a Huffman encoding tree according to the Huffman +algorithm. + + +(define (generate-huffman-tree pairs) + (successive-merge + (make-leaf-set pairs))) + +Make-leaf-set is the procedure given above that transforms the list of +pairs into an ordered set of leaves. Successive-merge is the procedure +you must write, using make-code-tree to successively merge the +smallest-weight elements of the set until there is only one element left, which +is the desired Huffman tree. (This procedure is slightly tricky, but not +really complicated. If you find yourself designing a complex procedure, then +you are almost certainly doing something wrong. You can take significant +advantage of the fact that we are using an ordered set representation.) diff --git a/sicp/2_002e7 b/sicp/2_002e7 new file mode 100644 index 0000000..52a5c09 --- /dev/null +++ b/sicp/2_002e7 @@ -0,0 +1,10 @@ + +Exercise 2.7: Alyssa’s program is incomplete +because she has not specified the implementation of the interval abstraction. +Here is a definition of the interval constructor: + + +(define (make-interval a b) (cons a b)) + +Define selectors upper-bound and lower-bound to complete the +implementation. diff --git a/sicp/2_002e70 b/sicp/2_002e70 new file mode 100644 index 0000000..32cacbe --- /dev/null +++ b/sicp/2_002e70 @@ -0,0 +1,32 @@ + +Exercise 2.70: The following eight-symbol +alphabet with associated relative frequencies was designed to efficiently +encode the lyrics of 1950s rock songs. (Note that the “symbols” of an +“alphabet” need not be individual letters.) + + +A 2 NA 16 +BOOM 1 SHA 3 +GET 2 YIP 9 +JOB 2 WAH 1 + + +Use generate-huffman-tree (Exercise 2.69) to generate a +corresponding Huffman tree, and use encode (Exercise 2.68) to +encode the following message: + + +Get a job +Sha na na na na na na na na + +Get a job +Sha na na na na na na na na + +Wah yip yip yip yip +yip yip yip yip yip +Sha boom + + +How many bits are required for the encoding? What is the smallest number of +bits that would be needed to encode this song if we used a fixed-length code +for the eight-symbol alphabet? diff --git a/sicp/2_002e71 b/sicp/2_002e71 new file mode 100644 index 0000000..fad2fdf --- /dev/null +++ b/sicp/2_002e71 @@ -0,0 +1,41 @@ + +Exercise 2.71: Suppose we have a Huffman tree +for an alphabet of + n + symbols, and that the relative frequencies of the +symbols are + + 1 + , + 2 + , + 4 + , + … + , + + 2 + + n + − + 1 + + + +. Sketch the tree for + + n + = + 5 + +; for + + + n + = + 10 + +. In such a tree (for general + n +) how many bits are required to +encode the most frequent symbol? The least frequent symbol? diff --git a/sicp/2_002e72 b/sicp/2_002e72 new file mode 100644 index 0000000..2710b0b --- /dev/null +++ b/sicp/2_002e72 @@ -0,0 +1,14 @@ + +Exercise 2.72: Consider the encoding procedure +that you designed in Exercise 2.68. What is the order of growth in the +number of steps needed to encode a symbol? Be sure to include the number of +steps needed to search the symbol list at each node encountered. To answer +this question in general is difficult. Consider the special case where the +relative frequencies of the + n + symbols are as described in Exercise 2.71, +and give the order of growth (as a function of + n +) of the number of +steps needed to encode the most frequent and least frequent symbols in the +alphabet. diff --git a/sicp/2_002e73 b/sicp/2_002e73 new file mode 100644 index 0000000..4dcfe08 --- /dev/null +++ b/sicp/2_002e73 @@ -0,0 +1,66 @@ + +Exercise 2.73: 2.3.2 described a +program that performs symbolic differentiation: + + +(define (deriv exp var) + (cond ((number? exp) 0) + ((variable? exp) + (if (same-variable? exp var) 1 0)) + ((sum? exp) + (make-sum (deriv (addend exp) var) + (deriv (augend exp) var))) + ((product? exp) + (make-sum + (make-product + (multiplier exp) + (deriv (multiplicand exp) var)) + (make-product + (deriv (multiplier exp) var) + (multiplicand exp)))) + ⟨more rules can be added here⟩ + (else (error "unknown expression type: + DERIV" exp)))) + +We can regard this program as performing a dispatch on the type of the +expression to be differentiated. In this situation the “type tag” of the +datum is the algebraic operator symbol (such as +) and the operation +being performed is deriv. We can transform this program into +data-directed style by rewriting the basic derivative procedure as + + +(define (deriv exp var) + (cond ((number? exp) 0) + ((variable? exp) + (if (same-variable? exp var) + 1 + 0)) + (else ((get 'deriv (operator exp)) + (operands exp) + var)))) + +(define (operator exp) (car exp)) +(define (operands exp) (cdr exp)) + + + Explain what was done above. Why can’t we assimilate the predicates +number? and variable? into the data-directed dispatch? + + Write the procedures for derivatives of sums and products, and the auxiliary +code required to install them in the table used by the program above. + + Choose any additional differentiation rule that you like, such as the one for +exponents (Exercise 2.56), and install it in this data-directed +system. + + In this simple algebraic manipulator the type of an expression is the algebraic +operator that binds it together. Suppose, however, we indexed the procedures +in the opposite way, so that the dispatch line in deriv looked like + + +((get (operator exp) 'deriv) + (operands exp) var) + +What corresponding changes to the derivative system are required? + + diff --git a/sicp/2_002e74 b/sicp/2_002e74 new file mode 100644 index 0000000..eb63c8a --- /dev/null +++ b/sicp/2_002e74 @@ -0,0 +1,43 @@ + +Exercise 2.74: Insatiable Enterprises, Inc., is +a highly decentralized conglomerate company consisting of a large number of +independent divisions located all over the world. The company’s computer +facilities have just been interconnected by means of a clever +network-interfacing scheme that makes the entire network appear to any user to +be a single computer. Insatiable’s president, in her first attempt to exploit +the ability of the network to extract administrative information from division +files, is dismayed to discover that, although all the division files have been +implemented as data structures in Scheme, the particular data structure used +varies from division to division. A meeting of division managers is hastily +called to search for a strategy to integrate the files that will satisfy +headquarters’ needs while preserving the existing autonomy of the divisions. + +Show how such a strategy can be implemented with data-directed programming. As +an example, suppose that each division’s personnel records consist of a single +file, which contains a set of records keyed on employees’ names. The structure +of the set varies from division to division. Furthermore, each employee’s +record is itself a set (structured differently from division to division) that +contains information keyed under identifiers such as address and +salary. In particular: + + + Implement for headquarters a get-record procedure that retrieves a +specified employee’s record from a specified personnel file. The procedure +should be applicable to any division’s file. Explain how the individual +divisions’ files should be structured. In particular, what type information +must be supplied? + + Implement for headquarters a get-salary procedure that returns the +salary information from a given employee’s record from any division’s personnel +file. How should the record be structured in order to make this operation +work? + + Implement for headquarters a find-employee-record procedure. This +should search all the divisions’ files for the record of a given employee and +return the record. Assume that this procedure takes as arguments an employee’s +name and a list of all the divisions’ files. + + When Insatiable takes over a new company, what changes must be made in order to +incorporate the new personnel information into the central system? + + diff --git a/sicp/2_002e75 b/sicp/2_002e75 new file mode 100644 index 0000000..1b35eaf --- /dev/null +++ b/sicp/2_002e75 @@ -0,0 +1,4 @@ + +Exercise 2.75: Implement the constructor +make-from-mag-ang in message-passing style. This procedure should be +analogous to the make-from-real-imag procedure given above. diff --git a/sicp/2_002e76 b/sicp/2_002e76 new file mode 100644 index 0000000..f60881a --- /dev/null +++ b/sicp/2_002e76 @@ -0,0 +1,9 @@ + +Exercise 2.76: As a large system with generic +operations evolves, new types of data objects or new operations may be needed. +For each of the three strategies—generic operations with explicit dispatch, +data-directed style, and message-passing-style—describe the changes that +must be made to a system in order to add new types or new operations. Which +organization would be most appropriate for a system in which new types must +often be added? Which would be most appropriate for a system in which new +operations must often be added? diff --git a/sicp/2_002e77 b/sicp/2_002e77 new file mode 100644 index 0000000..082554c --- /dev/null +++ b/sicp/2_002e77 @@ -0,0 +1,22 @@ + +Exercise 2.77: Louis Reasoner tries to evaluate +the expression (magnitude z) where z is the object shown in +Figure 2.24. To his surprise, instead of the answer 5 he gets an error +message from apply-generic, saying there is no method for the operation +magnitude on the types (complex). He shows this interaction to +Alyssa P. Hacker, who says “The problem is that the complex-number selectors +were never defined for complex numbers, just for polar and +rectangular numbers. All you have to do to make this work is add the +following to the complex package:” + + +(put 'real-part '(complex) real-part) +(put 'imag-part '(complex) imag-part) +(put 'magnitude '(complex) magnitude) +(put 'angle '(complex) angle) + +Describe in detail why this works. As an example, trace through all the +procedures called in evaluating the expression (magnitude z) where +z is the object shown in Figure 2.24. In particular, how many +times is apply-generic invoked? What procedure is dispatched to in each +case? diff --git a/sicp/2_002e78 b/sicp/2_002e78 new file mode 100644 index 0000000..3584913 --- /dev/null +++ b/sicp/2_002e78 @@ -0,0 +1,14 @@ + +Exercise 2.78: The internal procedures in the +scheme-number package are essentially nothing more than calls to the +primitive procedures +, -, etc. It was not possible to use the +primitives of the language directly because our type-tag system requires that +each data object have a type attached to it. In fact, however, all Lisp +implementations do have a type system, which they use internally. Primitive +predicates such as symbol? and number? determine whether data +objects have particular types. Modify the definitions of type-tag, +contents, and attach-tag from 2.4.2 so that our +generic system takes advantage of Scheme’s internal type system. That is to +say, the system should work as before except that ordinary numbers should be +represented simply as Scheme numbers rather than as pairs whose car is +the symbol scheme-number. diff --git a/sicp/2_002e79 b/sicp/2_002e79 new file mode 100644 index 0000000..4052e05 --- /dev/null +++ b/sicp/2_002e79 @@ -0,0 +1,5 @@ + +Exercise 2.79: Define a generic equality +predicate equ? that tests the equality of two numbers, and install it in +the generic arithmetic package. This operation should work for ordinary +numbers, rational numbers, and complex numbers. diff --git a/sicp/2_002e8 b/sicp/2_002e8 new file mode 100644 index 0000000..7a294a9 --- /dev/null +++ b/sicp/2_002e8 @@ -0,0 +1,4 @@ + +Exercise 2.8: Using reasoning analogous to +Alyssa’s, describe how the difference of two intervals may be computed. Define +a corresponding subtraction procedure, called sub-interval. diff --git a/sicp/2_002e80 b/sicp/2_002e80 new file mode 100644 index 0000000..e54143e --- /dev/null +++ b/sicp/2_002e80 @@ -0,0 +1,5 @@ + +Exercise 2.80: Define a generic predicate +=zero? that tests if its argument is zero, and install it in the generic +arithmetic package. This operation should work for ordinary numbers, rational +numbers, and complex numbers. diff --git a/sicp/2_002e81 b/sicp/2_002e81 new file mode 100644 index 0000000..5add8d4 --- /dev/null +++ b/sicp/2_002e81 @@ -0,0 +1,51 @@ + +Exercise 2.81: Louis Reasoner has noticed that +apply-generic may try to coerce the arguments to each other’s type even +if they already have the same type. Therefore, he reasons, we need to put +procedures in the coercion table to +coerce arguments of each type to +their own type. For example, in addition to the +scheme-number->complex coercion shown above, he would do: + + +(define (scheme-number->scheme-number n) n) +(define (complex->complex z) z) + +(put-coercion 'scheme-number 'scheme-number + scheme-number->scheme-number) + +(put-coercion 'complex 'complex + complex->complex) + + + With Louis’s coercion procedures installed, what happens if +apply-generic is called with two arguments of type scheme-number +or two arguments of type complex for an operation that is not found in +the table for those types? For example, assume that we’ve defined a generic +exponentiation operation: + + +(define (exp x y) + (apply-generic 'exp x y)) + +and have put a procedure for exponentiation in the Scheme-number +package but not in any other package: + + +;; following added to Scheme-number package +(put 'exp + '(scheme-number scheme-number) + (lambda (x y) + (tag (expt x y)))) + ; using primitive expt + + +What happens if we call exp with two complex numbers as arguments? + + Is Louis correct that something had to be done about coercion with arguments of +the same type, or does apply-generic work correctly as is? + + Modify apply-generic so that it doesn’t try coercion if the two +arguments have the same type. + + diff --git a/sicp/2_002e82 b/sicp/2_002e82 new file mode 100644 index 0000000..4bc9b9c --- /dev/null +++ b/sicp/2_002e82 @@ -0,0 +1,9 @@ + +Exercise 2.82: Show how to generalize +apply-generic to handle coercion in the general case of multiple +arguments. One strategy is to attempt to coerce all the arguments to the type +of the first argument, then to the type of the second argument, and so on. +Give an example of a situation where this strategy (and likewise the +two-argument version given above) is not sufficiently general. (Hint: Consider +the case where there are some suitable mixed-type operations present in the +table that will not be tried.) diff --git a/sicp/2_002e83 b/sicp/2_002e83 new file mode 100644 index 0000000..43f6f0f --- /dev/null +++ b/sicp/2_002e83 @@ -0,0 +1,7 @@ + +Exercise 2.83: Suppose you are designing a +generic arithmetic system for dealing with the tower of types shown in +Figure 2.25: integer, rational, real, complex. For each type (except +complex), design a procedure that raises objects of that type one level in the +tower. Show how to install a generic raise operation that will work for +each type (except complex). diff --git a/sicp/2_002e84 b/sicp/2_002e84 new file mode 100644 index 0000000..a9347d3 --- /dev/null +++ b/sicp/2_002e84 @@ -0,0 +1,8 @@ + +Exercise 2.84: Using the raise operation +of Exercise 2.83, modify the apply-generic procedure so that it +coerces its arguments to have the same type by the method of successive +raising, as discussed in this section. You will need to devise a way to test +which of two types is higher in the tower. Do this in a manner that is +“compatible” with the rest of the system and will not lead to problems in +adding new levels to the tower. diff --git a/sicp/2_002e85 b/sicp/2_002e85 new file mode 100644 index 0000000..620bead --- /dev/null +++ b/sicp/2_002e85 @@ -0,0 +1,42 @@ + +Exercise 2.85: This section mentioned a method +for “simplifying” a data object by lowering it in the tower of types as far +as possible. Design a procedure drop that accomplishes this for the +tower described in Exercise 2.83. The key is to decide, in some general +way, whether an object can be lowered. For example, the complex number + + + 1.5 + + + 0 + i + + can be lowered as far as real, the complex number + + 1 + + + 0 + i + + can +be lowered as far as integer, and the complex number + + 2 + + + 3 + i + + cannot +be lowered at all. Here is a plan for determining whether an object can be +lowered: Begin by defining a generic operation project that “pushes” +an object down in the tower. For example, projecting a complex number would +involve throwing away the imaginary part. Then a number can be dropped if, +when we project it and raise the result back to the type we +started with, we end up with something equal to what we started with. Show how +to implement this idea in detail, by writing a drop procedure that drops +an object as far as possible. You will need to design the various projection +operations119 and +install project as a generic operation in the system. You will also +need to make use of a generic equality predicate, such as described in +Exercise 2.79. Finally, use drop to rewrite apply-generic +from Exercise 2.84 so that it “simplifies” its answers. diff --git a/sicp/2_002e86 b/sicp/2_002e86 new file mode 100644 index 0000000..edeced9 --- /dev/null +++ b/sicp/2_002e86 @@ -0,0 +1,7 @@ + +Exercise 2.86: Suppose we want to handle complex +numbers whose real parts, imaginary parts, magnitudes, and angles can be either +ordinary numbers, rational numbers, or other numbers we might wish to add to +the system. Describe and implement the changes to the system needed to +accommodate this. You will have to define operations such as sine and +cosine that are generic over ordinary numbers and rational numbers. diff --git a/sicp/2_002e87 b/sicp/2_002e87 new file mode 100644 index 0000000..1b7a1af --- /dev/null +++ b/sicp/2_002e87 @@ -0,0 +1,5 @@ + +Exercise 2.87: Install =zero? for +polynomials in the generic arithmetic package. This will allow +adjoin-term to work for polynomials with coefficients that are +themselves polynomials. diff --git a/sicp/2_002e88 b/sicp/2_002e88 new file mode 100644 index 0000000..e105aa5 --- /dev/null +++ b/sicp/2_002e88 @@ -0,0 +1,4 @@ + +Exercise 2.88: Extend the polynomial system to +include subtraction of polynomials. (Hint: You may find it helpful to define a +generic negation operation.) diff --git a/sicp/2_002e89 b/sicp/2_002e89 new file mode 100644 index 0000000..bd9cb7f --- /dev/null +++ b/sicp/2_002e89 @@ -0,0 +1,4 @@ + +Exercise 2.89: Define procedures that implement +the term-list representation described above as appropriate for dense +polynomials. diff --git a/sicp/2_002e9 b/sicp/2_002e9 new file mode 100644 index 0000000..da12da5 --- /dev/null +++ b/sicp/2_002e9 @@ -0,0 +1,11 @@ + +Exercise 2.9: The +width of an interval +is half of the difference between its upper and lower bounds. The width is a +measure of the uncertainty of the number specified by the interval. For some +arithmetic operations the width of the result of combining two intervals is a +function only of the widths of the argument intervals, whereas for others the +width of the combination is not a function of the widths of the argument +intervals. Show that the width of the sum (or difference) of two intervals is +a function only of the widths of the intervals being added (or subtracted). +Give examples to show that this is not true for multiplication or division. diff --git a/sicp/2_002e90 b/sicp/2_002e90 new file mode 100644 index 0000000..7a3f859 --- /dev/null +++ b/sicp/2_002e90 @@ -0,0 +1,9 @@ + +Exercise 2.90: Suppose we want to have a +polynomial system that is efficient for both sparse and dense polynomials. One +way to do this is to allow both kinds of term-list representations in our +system. The situation is analogous to the complex-number example of +2.4, where we allowed both rectangular and polar representations. To do +this we must distinguish different types of term lists and make the operations +on term lists generic. Redesign the polynomial system to implement this +generalization. This is a major effort, not a local change. diff --git a/sicp/2_002e91 b/sicp/2_002e91 new file mode 100644 index 0000000..6952acf --- /dev/null +++ b/sicp/2_002e91 @@ -0,0 +1,84 @@ + +Exercise 2.91: A univariate polynomial can be +divided by another one to produce a polynomial quotient and a polynomial +remainder. For example, + + + + + + x + 5 + + − + 1 + + + + x + 2 + + − + 1 + + + + + = + + + + x + 3 + + + + x + , + +  remainder  + + x + − + 1. + + +Division can be performed via long division. That is, divide the highest-order +term of the dividend by the highest-order term of the divisor. The result is +the first term of the quotient. Next, multiply the result by the divisor, +subtract that from the dividend, and produce the rest of the answer by +recursively dividing the difference by the divisor. Stop when the order of the +divisor exceeds the order of the dividend and declare the dividend to be the +remainder. Also, if the dividend ever becomes zero, return zero as both +quotient and remainder. + +We can design a div-poly procedure on the model of add-poly and +mul-poly. The procedure checks to see if the two polys have the same +variable. If so, div-poly strips off the variable and passes the +problem to div-terms, which performs the division operation on term +lists. Div-poly finally reattaches the variable to the result supplied +by div-terms. It is convenient to design div-terms to compute +both the quotient and the remainder of a division. Div-terms can take +two term lists as arguments and return a list of the quotient term list and the +remainder term list. + +Complete the following definition of div-terms by filling in the missing +expressions. Use this to implement div-poly, which takes two polys as +arguments and returns a list of the quotient and remainder polys. + + +(define (div-terms L1 L2) + (if (empty-termlist? L1) + (list (the-empty-termlist) + (the-empty-termlist)) + (let ((t1 (first-term L1)) + (t2 (first-term L2))) + (if (> (order t2) (order t1)) + (list (the-empty-termlist) L1) + (let ((new-c (div (coeff t1) + (coeff t2))) + (new-o (- (order t1) + (order t2)))) + (let ((rest-of-result + ⟨compute rest of result + recursively⟩ )) + ⟨form complete result⟩ )))))) diff --git a/sicp/2_002e92 b/sicp/2_002e92 new file mode 100644 index 0000000..62d2a7e --- /dev/null +++ b/sicp/2_002e92 @@ -0,0 +1,4 @@ + +Exercise 2.92: By imposing an ordering on +variables, extend the polynomial package so that addition and multiplication of +polynomials works for polynomials in different variables. (This is not easy!) diff --git a/sicp/2_002e93 b/sicp/2_002e93 new file mode 100644 index 0000000..ce16c02 --- /dev/null +++ b/sicp/2_002e93 @@ -0,0 +1,13 @@ + +Exercise 2.93: Modify the rational-arithmetic +package to use generic operations, but change make-rat so that it does +not attempt to reduce fractions to lowest terms. Test your system by calling +make-rational on two polynomials to produce a rational function: + + +(define p1 (make-polynomial 'x '((2 1) (0 1)))) +(define p2 (make-polynomial 'x '((3 1) (0 1)))) +(define rf (make-rational p2 p1)) + +Now add rf to itself, using add. You will observe that this +addition procedure does not reduce fractions to lowest terms. diff --git a/sicp/2_002e94 b/sicp/2_002e94 new file mode 100644 index 0000000..00aeaa3 --- /dev/null +++ b/sicp/2_002e94 @@ -0,0 +1,21 @@ + +Exercise 2.94: Using div-terms, implement +the procedure remainder-terms and use this to define gcd-terms as +above. Now write a procedure gcd-poly that computes the polynomial +GCD of two polys. (The procedure should signal an error if the two +polys are not in the same variable.) Install in the system a generic operation +greatest-common-divisor that reduces to gcd-poly for polynomials +and to ordinary gcd for ordinary numbers. As a test, try + + +(define p1 + (make-polynomial + 'x '((4 1) (3 -1) (2 -2) (1 2)))) + +(define p2 + (make-polynomial + 'x '((3 1) (1 -1)))) + +(greatest-common-divisor p1 p2) + +and check your result by hand. diff --git a/sicp/2_002e95 b/sicp/2_002e95 new file mode 100644 index 0000000..b2b2f82 --- /dev/null +++ b/sicp/2_002e95 @@ -0,0 +1,130 @@ + +Exercise 2.95: Define + + P + 1 + +, + + P + 2 + +, and + + + P + 3 + + to be the polynomials + + + + + + P + 1 + + : + + + + x + 2 + + − + 2 + x + + + 1 + , + + + + + + P + 2 + + : + + + 11 + + x + 2 + + + + 7 + , + + + + + + P + 3 + + : + + + 13 + x + + + 5. + + + + +Now define + + Q + 1 + + to be the product of + + P + 1 + + and + + P + 2 + +, and + + Q + 2 + + to be +the product of + + P + 1 + + and + + P + 3 + +, and use greatest-common-divisor +(Exercise 2.94) to compute the GCD of + + Q + 1 + + and + + Q + 2 + +. +Note that the answer is not the same as + + P + 1 + +. This example introduces +noninteger operations into the computation, causing difficulties with the +GCD algorithm.127 To understand what is happening, try tracing +gcd-terms while computing the GCD or try performing the +division by hand. diff --git a/sicp/2_002e96 b/sicp/2_002e96 new file mode 100644 index 0000000..3bb4e9e --- /dev/null +++ b/sicp/2_002e96 @@ -0,0 +1,21 @@ + +Exercise 2.96: + + Implement the procedure pseudoremainder-terms, which is just like +remainder-terms except that it multiplies the dividend by the +integerizing factor described above before calling div-terms. Modify +gcd-terms to use pseudoremainder-terms, and verify that +greatest-common-divisor now produces an answer with integer coefficients +on the example in Exercise 2.95. + + The GCD now has integer coefficients, but they are larger than those +of + + P + 1 + +. Modify gcd-terms so that it removes common factors from the +coefficients of the answer by dividing all the coefficients by their (integer) +greatest common divisor. + + diff --git a/sicp/2_002e97 b/sicp/2_002e97 new file mode 100644 index 0000000..664b374 --- /dev/null +++ b/sicp/2_002e97 @@ -0,0 +1,44 @@ + +Exercise 2.97: + + Implement this algorithm as a procedure reduce-terms that takes two term +lists n and d as arguments and returns a list nn, +dd, which are n and d reduced to lowest terms via the +algorithm given above. Also write a procedure reduce-poly, analogous to +add-poly, that checks to see if the two polys have the same variable. +If so, reduce-poly strips off the variable and passes the problem to +reduce-terms, then reattaches the variable to the two term lists +supplied by reduce-terms. + + Define a procedure analogous to reduce-terms that does what the original +make-rat did for integers: + + +(define (reduce-integers n d) + (let ((g (gcd n d))) + (list (/ n g) (/ d g)))) + +and define reduce as a generic operation that calls apply-generic +to dispatch to either reduce-poly (for polynomial arguments) or +reduce-integers (for scheme-number arguments). You can now +easily make the rational-arithmetic package reduce fractions to lowest terms by +having make-rat call reduce before combining the given numerator +and denominator to form a rational number. The system now handles rational +expressions in either integers or polynomials. To test your program, try the +example at the beginning of this extended exercise: + + +(define p1 + (make-polynomial 'x '((1 1) (0 1)))) +(define p2 + (make-polynomial 'x '((3 1) (0 -1)))) +(define p3 + (make-polynomial 'x '((1 1)))) +(define p4 + (make-polynomial 'x '((2 1) (0 -1)))) +(define rf1 (make-rational p1 p2)) +(define rf2 (make-rational p3 p4)) +(add rf1 rf2) + +See if you get the correct answer, correctly reduced to lowest terms. + diff --git a/sicp/3_002e1 b/sicp/3_002e1 new file mode 100644 index 0000000..40c0aa5 --- /dev/null +++ b/sicp/3_002e1 @@ -0,0 +1,19 @@ + +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; for +example + + +(define A (make-accumulator 5)) + +(A 10) +15 + +(A 10) +25 + diff --git a/sicp/3_002e10 b/sicp/3_002e10 new file mode 100644 index 0000000..b71282a --- /dev/null +++ b/sicp/3_002e10 @@ -0,0 +1,38 @@ + +Exercise 3.10: In the make-withdraw +procedure, the local variable balance is created as a parameter of +make-withdraw. We could also create the local state variable +explicitly, using let, as follows: + + +(define (make-withdraw initial-amount) + (let ((balance initial-amount)) + (lambda (amount) + (if (>= balance amount) + (begin (set! balance + (- balance amount)) + balance) + "Insufficient funds")))) + +Recall from 1.3.2 that let is simply syntactic sugar for a +procedure call: + + +(let ((⟨var⟩ ⟨exp⟩)) ⟨body⟩) + +is interpreted as an alternate syntax for + + +((lambda (⟨var⟩) ⟨body⟩) ⟨exp⟩) + +Use the environment model to analyze this alternate version of +make-withdraw, drawing figures like the ones above to illustrate the +interactions + + +(define W1 (make-withdraw 100)) +(W1 50) +(define W2 (make-withdraw 100)) + +Show that the two versions of make-withdraw create objects with the same +behavior. How do the environment structures differ for the two versions? diff --git a/sicp/3_002e11 b/sicp/3_002e11 new file mode 100644 index 0000000..ab87759 --- /dev/null +++ b/sicp/3_002e11 @@ -0,0 +1,47 @@ + +Exercise 3.11: In 3.2.3 we saw how +the environment model described the behavior of procedures with local state. +Now we have seen how internal definitions work. A typical message-passing +procedure contains both of these aspects. Consider the bank account procedure +of 3.1.1: + + +(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) + (define (dispatch m) + (cond ((eq? m 'withdraw) withdraw) + ((eq? m 'deposit) deposit) + (else (error "Unknown request: + MAKE-ACCOUNT" + m)))) + dispatch) + +Show the environment structure generated by the sequence of interactions + + +(define acc (make-account 50)) + +((acc 'deposit) 40) +90 + +((acc 'withdraw) 60) +30 + + +Where is the local state for acc kept? Suppose we define another +account + + +(define acc2 (make-account 100)) + +How are the local states for the two accounts kept distinct? Which parts of +the environment structure are shared between acc and acc2? diff --git a/sicp/3_002e12 b/sicp/3_002e12 new file mode 100644 index 0000000..d68ca83 --- /dev/null +++ b/sicp/3_002e12 @@ -0,0 +1,54 @@ + +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.) + + +(define (append! x y) + (set-cdr! (last-pair x) y) + x) + +Here last-pair is a procedure that returns the last pair in its +argument: + + +(define (last-pair x) + (if (null? (cdr x)) + x + (last-pair (cdr x)))) + +Consider the interaction + + +(define x (list 'a 'b)) +(define y (list 'c 'd)) +(define z (append x y)) + +z +(a b c d) + +(cdr x) +⟨response⟩ + +(define w (append! x y)) + +w +(a b c d) + +(cdr x) +⟨response⟩ + +What are the missing ⟨response⟩s? Draw box-and-pointer diagrams to +explain your answer. diff --git a/sicp/3_002e13 b/sicp/3_002e13 new file mode 100644 index 0000000..a35e817 --- /dev/null +++ b/sicp/3_002e13 @@ -0,0 +1,16 @@ + +Exercise 3.13: Consider the following +make-cycle procedure, which uses the last-pair procedure defined +in Exercise 3.12: + + +(define (make-cycle x) + (set-cdr! (last-pair x) x) + x) + +Draw a box-and-pointer diagram that shows the structure z created by + + +(define z (make-cycle (list 'a 'b 'c))) + +What happens if we try to compute (last-pair z)? diff --git a/sicp/3_002e14 b/sicp/3_002e14 new file mode 100644 index 0000000..f1573e7 --- /dev/null +++ b/sicp/3_002e14 @@ -0,0 +1,23 @@ + +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? diff --git a/sicp/3_002e15 b/sicp/3_002e15 new file mode 100644 index 0000000..81bf4a6 --- /dev/null +++ b/sicp/3_002e15 @@ -0,0 +1,4 @@ + +Exercise 3.15: Draw box-and-pointer diagrams to +explain the effect of set-to-wow! on the structures z1 and +z2 above. diff --git a/sicp/3_002e16 b/sicp/3_002e16 new file mode 100644 index 0000000..23feab8 --- /dev/null +++ b/sicp/3_002e16 @@ -0,0 +1,18 @@ + +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. diff --git a/sicp/3_002e17 b/sicp/3_002e17 new file mode 100644 index 0000000..c223522 --- /dev/null +++ b/sicp/3_002e17 @@ -0,0 +1,6 @@ + +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.) diff --git a/sicp/3_002e18 b/sicp/3_002e18 new file mode 100644 index 0000000..b1f044a --- /dev/null +++ b/sicp/3_002e18 @@ -0,0 +1,5 @@ + +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. diff --git a/sicp/3_002e19 b/sicp/3_002e19 new file mode 100644 index 0000000..2d6c92a --- /dev/null +++ b/sicp/3_002e19 @@ -0,0 +1,4 @@ + +Exercise 3.19: Redo Exercise 3.18 using an +algorithm that takes only a constant amount of space. (This requires a very +clever idea.) diff --git a/sicp/3_002e2 b/sicp/3_002e2 new file mode 100644 index 0000000..5f28ba6 --- /dev/null +++ b/sicp/3_002e2 @@ -0,0 +1,23 @@ + +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: + + +(define s (make-monitored sqrt)) + +(s 100) +10 + +(s 'how-many-calls?) +1 + diff --git a/sicp/3_002e20 b/sicp/3_002e20 new file mode 100644 index 0000000..938996d --- /dev/null +++ b/sicp/3_002e20 @@ -0,0 +1,16 @@ + +Exercise 3.20: Draw environment diagrams to +illustrate the evaluation of the sequence of expressions + + +(define x (cons 1 2)) +(define z (cons x x)) + +(set-car! (cdr z) 17) + +(car x) +17 + + +using the procedural implementation of pairs given above. (Compare +Exercise 3.11.) diff --git a/sicp/3_002e21 b/sicp/3_002e21 new file mode 100644 index 0000000..cfb2c11 --- /dev/null +++ b/sicp/3_002e21 @@ -0,0 +1,32 @@ + +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: + + +(define q1 (make-queue)) + +(insert-queue! q1 'a) +((a) a) + +(insert-queue! q1 'b) +((a b) b) + +(delete-queue! q1) +((b) b) + +(delete-queue! q1) +(() b) + + +“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. diff --git a/sicp/3_002e22 b/sicp/3_002e22 new file mode 100644 index 0000000..5fae5a1 --- /dev/null +++ b/sicp/3_002e22 @@ -0,0 +1,16 @@ + +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. diff --git a/sicp/3_002e23 b/sicp/3_002e23 new file mode 100644 index 0000000..785a9c1 --- /dev/null +++ b/sicp/3_002e23 @@ -0,0 +1,18 @@ + +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.151 +All operations should be accomplished in + + Θ + ( + 1 + ) + + steps. diff --git a/sicp/3_002e24 b/sicp/3_002e24 new file mode 100644 index 0000000..d47a656 --- /dev/null +++ b/sicp/3_002e24 @@ -0,0 +1,11 @@ + +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. diff --git a/sicp/3_002e25 b/sicp/3_002e25 new file mode 100644 index 0000000..3b7b977 --- /dev/null +++ b/sicp/3_002e25 @@ -0,0 +1,6 @@ + +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. diff --git a/sicp/3_002e26 b/sicp/3_002e26 new file mode 100644 index 0000000..343b0be --- /dev/null +++ b/sicp/3_002e26 @@ -0,0 +1,8 @@ + +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 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.) diff --git a/sicp/3_002e27 b/sicp/3_002e27 new file mode 100644 index 0000000..9062d3c --- /dev/null +++ b/sicp/3_002e27 @@ -0,0 +1,60 @@ + +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 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 + + n + + th + + + 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)? diff --git a/sicp/3_002e28 b/sicp/3_002e28 new file mode 100644 index 0000000..93eca9e --- /dev/null +++ b/sicp/3_002e28 @@ -0,0 +1,4 @@ + +Exercise 3.28: Define an or-gate as a primitive +function box. Your or-gate constructor should be similar to +and-gate. diff --git a/sicp/3_002e29 b/sicp/3_002e29 new file mode 100644 index 0000000..7d384b7 --- /dev/null +++ b/sicp/3_002e29 @@ -0,0 +1,6 @@ + +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? diff --git a/sicp/3_002e3 b/sicp/3_002e3 new file mode 100644 index 0000000..dc16d43 --- /dev/null +++ b/sicp/3_002e3 @@ -0,0 +1,20 @@ + +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 + + +(define acc + (make-account 100 'secret-password)) + +The resulting account object should process a request only if it is accompanied +by the password with which the account was created, and should otherwise return +a complaint: + + +((acc 'secret-password 'withdraw) 40) +60 + +((acc 'some-other-password 'deposit) 50) +"Incorrect password" + diff --git a/sicp/3_002e30 b/sicp/3_002e30 new file mode 100644 index 0000000..fbc430a --- /dev/null +++ b/sicp/3_002e30 @@ -0,0 +1,117 @@ + +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? diff --git a/sicp/3_002e31 b/sicp/3_002e31 new file mode 100644 index 0000000..f31088e --- /dev/null +++ b/sicp/3_002e31 @@ -0,0 +1,12 @@ + +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))) diff --git a/sicp/3_002e32 b/sicp/3_002e32 new file mode 100644 index 0000000..48a4818 --- /dev/null +++ b/sicp/3_002e32 @@ -0,0 +1,9 @@ + +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). diff --git a/sicp/3_002e33 b/sicp/3_002e33 new file mode 100644 index 0000000..466d973 --- /dev/null +++ b/sicp/3_002e33 @@ -0,0 +1,6 @@ + +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. diff --git a/sicp/3_002e34 b/sicp/3_002e34 new file mode 100644 index 0000000..dd465a1 --- /dev/null +++ b/sicp/3_002e34 @@ -0,0 +1,11 @@ + +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. diff --git a/sicp/3_002e35 b/sicp/3_002e35 new file mode 100644 index 0000000..1d5d109 --- /dev/null +++ b/sicp/3_002e35 @@ -0,0 +1,20 @@ + +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) diff --git a/sicp/3_002e36 b/sicp/3_002e36 new file mode 100644 index 0000000..feea1cb --- /dev/null +++ b/sicp/3_002e36 @@ -0,0 +1,18 @@ + +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. diff --git a/sicp/3_002e37 b/sicp/3_002e37 new file mode 100644 index 0000000..3f8bcf2 --- /dev/null +++ b/sicp/3_002e37 @@ -0,0 +1,28 @@ + +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.161 diff --git a/sicp/3_002e38 b/sicp/3_002e38 new file mode 100644 index 0000000..40a3ecf --- /dev/null +++ b/sicp/3_002e38 @@ -0,0 +1,23 @@ + +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))) + + + + 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. + + 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. + + diff --git a/sicp/3_002e39 b/sicp/3_002e39 new file mode 100644 index 0000000..05a550e --- /dev/null +++ b/sicp/3_002e39 @@ -0,0 +1,12 @@ + +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))))) diff --git a/sicp/3_002e4 b/sicp/3_002e4 new file mode 100644 index 0000000..cd936e9 --- /dev/null +++ b/sicp/3_002e4 @@ -0,0 +1,5 @@ + +Exercise 3.4: Modify the make-account +procedure of Exercise 3.3 by adding another local state variable so that, +if an account is accessed more than seven consecutive times with an incorrect +password, it invokes the procedure call-the-cops. diff --git a/sicp/3_002e40 b/sicp/3_002e40 new file mode 100644 index 0000000..1c81b0a --- /dev/null +++ b/sicp/3_002e40 @@ -0,0 +1,18 @@ + +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))))) diff --git a/sicp/3_002e41 b/sicp/3_002e41 new file mode 100644 index 0000000..14ef789 --- /dev/null +++ b/sicp/3_002e41 @@ -0,0 +1,37 @@ + +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? diff --git a/sicp/3_002e42 b/sicp/3_002e42 new file mode 100644 index 0000000..5c4730f --- /dev/null +++ b/sicp/3_002e42 @@ -0,0 +1,40 @@ + +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))) + +Is this a safe change to make? In particular, is there any difference in what +concurrency is allowed by these two versions of make-account? diff --git a/sicp/3_002e43 b/sicp/3_002e43 new file mode 100644 index 0000000..dd2b74c --- /dev/null +++ b/sicp/3_002e43 @@ -0,0 +1,12 @@ + +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 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. diff --git a/sicp/3_002e44 b/sicp/3_002e44 new file mode 100644 index 0000000..a754bdf --- /dev/null +++ b/sicp/3_002e44 @@ -0,0 +1,19 @@ + +Exercise 3.44: Consider the problem of +transferring an amount from one account to another. Ben Bitdiddle claims that +this can be accomplished with the following procedure, even if there are +multiple people concurrently transferring money among multiple accounts, using +any account mechanism that serializes deposit and withdrawal transactions, for +example, the version of make-account in the text above. + + +(define + (transfer from-account to-account amount) + ((from-account 'withdraw) amount) + ((to-account 'deposit) amount)) + +Louis Reasoner claims that there is a problem here, and that we need to use a +more sophisticated method, such as the one required for dealing with the +exchange problem. Is Louis right? If not, what is the essential difference +between the transfer problem and the exchange problem? (You should assume that +the balance in from-account is at least amount.) diff --git a/sicp/3_002e45 b/sicp/3_002e45 new file mode 100644 index 0000000..84cf5d6 --- /dev/null +++ b/sicp/3_002e45 @@ -0,0 +1,45 @@ + +Exercise 3.45: Louis Reasoner thinks our +bank-account system is unnecessarily complex and error-prone now that deposits +and withdrawals aren’t automatically serialized. He suggests that +make-account-and-serializer should have exported the serializer (for use +by such procedures as serialized-exchange) in addition to (rather than +instead of) using it to serialize accounts and deposits as make-account +did. He proposes to redefine accounts as follows: + + +(define + (make-account-and-serializer balance) + (define (withdraw amount) + (if (>= balance amount) + (begin (set! balance + (- balance amount)) + balance) + "Insufficient funds")) + (define (deposit amount) + (set! balance (+ balance amount)) + balance) + (let ((balance-serializer + (make-serializer))) + (define (dispatch m) + (cond ((eq? m 'withdraw) + (balance-serializer withdraw)) + ((eq? m 'deposit) + (balance-serializer deposit)) + ((eq? m 'balance) + balance) + ((eq? m 'serializer) + balance-serializer) + (else (error "Unknown request: + MAKE-ACCOUNT" + m)))) + dispatch)) + +Then deposits are handled as with the original make-account: + + +(define (deposit account amount) + ((account 'deposit) amount)) + +Explain what is wrong with Louis’s reasoning. In particular, consider what +happens when serialized-exchange is called. diff --git a/sicp/3_002e46 b/sicp/3_002e46 new file mode 100644 index 0000000..7447650 --- /dev/null +++ b/sicp/3_002e46 @@ -0,0 +1,6 @@ + +Exercise 3.46: Suppose that we implement +test-and-set! using an ordinary procedure as shown in the text, without +attempting to make the operation atomic. Draw a timing diagram like the one in +Figure 3.29 to demonstrate how the mutex implementation can fail by +allowing two processes to acquire the mutex at the same time. diff --git a/sicp/3_002e47 b/sicp/3_002e47 new file mode 100644 index 0000000..9c101af --- /dev/null +++ b/sicp/3_002e47 @@ -0,0 +1,17 @@ + +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 + + + in terms of mutexes + + in terms of atomic test-and-set! operations. + + diff --git a/sicp/3_002e48 b/sicp/3_002e48 new file mode 100644 index 0000000..85d01e4 --- /dev/null +++ b/sicp/3_002e48 @@ -0,0 +1,8 @@ + +Exercise 3.48: Explain in detail why the +deadlock-avoidance method described above, (i.e., the accounts are numbered, +and each process attempts to acquire the smaller-numbered account first) avoids +deadlock in the exchange problem. Rewrite serialized-exchange to +incorporate this idea. (You will also need to modify make-account so +that each account is created with a number, which can be accessed by sending an +appropriate message.) diff --git a/sicp/3_002e49 b/sicp/3_002e49 new file mode 100644 index 0000000..cf78017 --- /dev/null +++ b/sicp/3_002e49 @@ -0,0 +1,7 @@ + +Exercise 3.49: Give a scenario where the +deadlock-avoidance mechanism described above does not work. (Hint: In the +exchange problem, each process knows in advance which accounts it will need to +get access to. Consider a situation where a process must get access to some +shared resources before it can know which additional shared resources it will +require.) diff --git a/sicp/3_002e5 b/sicp/3_002e5 new file mode 100644 index 0000000..d0b49b4 --- /dev/null +++ b/sicp/3_002e5 @@ -0,0 +1,104 @@ + +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 false 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 + + +(define (random-in-range low high) + (let ((range (- high low))) + (+ low (random range)))) diff --git a/sicp/3_002e50 b/sicp/3_002e50 new file mode 100644 index 0000000..c5b11ce --- /dev/null +++ b/sicp/3_002e50 @@ -0,0 +1,16 @@ + +Exercise 3.50: Complete the following +definition, which generalizes stream-map to allow procedures that take +multiple arguments, analogous to map in 2.2.1, +Footnote 78. + + +(define (stream-map proc . argstreams) + (if (⟨??⟩ (car argstreams)) + the-empty-stream + (⟨??⟩ + (apply proc (map ⟨??⟩ argstreams)) + (apply stream-map + (cons proc + (map ⟨??⟩ + argstreams)))))) diff --git a/sicp/3_002e51 b/sicp/3_002e51 new file mode 100644 index 0000000..8fbcfcf --- /dev/null +++ b/sicp/3_002e51 @@ -0,0 +1,21 @@ + +Exercise 3.51: In order to take a closer look at +delayed evaluation, we will use the following procedure, which simply returns +its argument after printing it: + + +(define (show x) + (display-line x) + x) + +What does the interpreter print in response to evaluating each expression in +the following sequence?187 + + +(define x + (stream-map + show + (stream-enumerate-interval 0 10))) + +(stream-ref x 5) +(stream-ref x 7) diff --git a/sicp/3_002e52 b/sicp/3_002e52 new file mode 100644 index 0000000..16f2e4f --- /dev/null +++ b/sicp/3_002e52 @@ -0,0 +1,31 @@ + +Exercise 3.52: Consider the sequence of +expressions + + +(define sum 0) + +(define (accum x) + (set! sum (+ x sum)) + sum) + +(define seq + (stream-map + accum + (stream-enumerate-interval 1 20))) + +(define y (stream-filter even? seq)) + +(define z + (stream-filter + (lambda (x) + (= (remainder x 5) 0)) seq)) + +(stream-ref y 7) +(display-stream z) + +What is the value of sum after each of the above expressions is +evaluated? What is the printed response to evaluating the stream-ref +and display-stream expressions? Would these responses differ if we had +implemented (delay ⟨exp⟩) simply as (lambda () ⟨exp⟩) +without using the optimization provided by memo-proc? Explain. diff --git a/sicp/3_002e53 b/sicp/3_002e53 new file mode 100644 index 0000000..b79429e --- /dev/null +++ b/sicp/3_002e53 @@ -0,0 +1,6 @@ + +Exercise 3.53: Without running the program, +describe the elements of the stream defined by + + +(define s (cons-stream 1 (add-streams s s))) diff --git a/sicp/3_002e54 b/sicp/3_002e54 new file mode 100644 index 0000000..cfa3f0a --- /dev/null +++ b/sicp/3_002e54 @@ -0,0 +1,23 @@ + +Exercise 3.54: Define a procedure +mul-streams, analogous to add-streams, that produces the +elementwise product of its two input streams. Use this together with the +stream of integers to complete the following definition of the stream +whose + + n + + th + + + element (counting from 0) is + + n + + + 1 + + factorial: + + +(define factorials + (cons-stream 1 (mul-streams ⟨??⟩ ⟨??⟩))) diff --git a/sicp/3_002e55 b/sicp/3_002e55 new file mode 100644 index 0000000..5102e3d --- /dev/null +++ b/sicp/3_002e55 @@ -0,0 +1,46 @@ + +Exercise 3.55: Define a procedure +partial-sums that takes as argument a stream + S + and returns the +stream whose elements are + + S + 0 + +, + + + S + 0 + + + + + S + 1 + + +, + + + S + 0 + + + + + S + 1 + + + + + + + S + 2 + + , + … + +. +For example, (partial-sums integers) should be the +stream 1, 3, 6, 10, 15, …. diff --git a/sicp/3_002e56 b/sicp/3_002e56 new file mode 100644 index 0000000..0761fb4 --- /dev/null +++ b/sicp/3_002e56 @@ -0,0 +1,57 @@ + +Exercise 3.56: A famous problem, first raised by +R. Hamming, is to enumerate, in ascending order with no repetitions, all +positive integers with no prime factors other than 2, 3, or 5. One obvious way +to do this is to simply test each integer in turn to see whether it has any +factors other than 2, 3, and 5. But this is very inefficient, since, as the +integers get larger, fewer and fewer of them fit the requirement. As an +alternative, let us call the required stream of numbers S and notice the +following facts about it. + + + S begins with 1. + + The elements of (scale-stream S 2) are also +elements of S. + + The same is true for (scale-stream S 3) +and (scale-stream S 5). + + These are all the elements of S. + + + +Now all we have to do is combine elements from these sources. For this we +define a procedure merge that combines two ordered streams into one +ordered result stream, eliminating repetitions: + + +(define (merge s1 s2) + (cond ((stream-null? s1) s2) + ((stream-null? s2) s1) + (else + (let ((s1car (stream-car s1)) + (s2car (stream-car s2))) + (cond ((< s1car s2car) + (cons-stream + s1car + (merge (stream-cdr s1) + s2))) + ((> s1car s2car) + (cons-stream + s2car + (merge s1 + (stream-cdr s2)))) + (else + (cons-stream + s1car + (merge + (stream-cdr s1) + (stream-cdr s2))))))))) + +Then the required stream may be constructed with merge, as follows: + + +(define S (cons-stream 1 (merge ⟨??⟩ ⟨??⟩))) + +Fill in the missing expressions in the places marked ⟨??⟩ above. diff --git a/sicp/3_002e57 b/sicp/3_002e57 new file mode 100644 index 0000000..91f400e --- /dev/null +++ b/sicp/3_002e57 @@ -0,0 +1,15 @@ + +Exercise 3.57: How many additions are performed +when we compute the + + n + + th + + + Fibonacci number using the definition of +fibs based on the add-streams procedure? Show that the number of +additions would be exponentially greater if we had implemented (delay ⟨exp⟩) +simply as (lambda () ⟨exp⟩), without using the +optimization provided by the memo-proc procedure described in +3.5.1.192 diff --git a/sicp/3_002e58 b/sicp/3_002e58 new file mode 100644 index 0000000..ac462e3 --- /dev/null +++ b/sicp/3_002e58 @@ -0,0 +1,15 @@ + +Exercise 3.58: Give an interpretation of the +stream computed by the following procedure: + + +(define (expand num den radix) + (cons-stream + (quotient (* num radix) den) + (expand (remainder (* num radix) den) + den + radix))) + +(Quotient is a primitive that returns the integer quotient of two +integers.) What are the successive elements produced by (expand 1 7 +10)? What is produced by (expand 3 8 10)? diff --git a/sicp/3_002e59 b/sicp/3_002e59 new file mode 100644 index 0000000..be21b40 --- /dev/null +++ b/sicp/3_002e59 @@ -0,0 +1,417 @@ + +Exercise 3.59: In 2.5.3 we saw how +to implement a polynomial arithmetic system representing polynomials as lists +of terms. In a similar way, we can work with +power series, such as + + + + + + e + x + + + + = + + + 1 + + + x + + + + 1 + 2 + + + x + 2 + + + + + 1 + + 3 + ⋅ + 2 + + + + x + 3 + + + + + 1 + + 4 + ⋅ + 3 + ⋅ + 2 + + + + x + 4 + + + + … + , + + + + + cos + ⁡ + x + + + = + + + 1 + − + + 1 + 2 + + + x + 2 + + + + + 1 + + 4 + ⋅ + 3 + ⋅ + 2 + + + + x + 4 + + − + … + , + + + + + sin + ⁡ + x + + + = + + + x + − + + 1 + + 3 + ⋅ + 2 + + + + x + 3 + + + + + 1 + + 5 + ⋅ + 4 + ⋅ + 3 + ⋅ + 2 + + + + x + 5 + + − + … + + + + +represented as infinite streams. We will represent the series + + + a + 0 + + + + + a + 1 + + x + + + + + + a + 2 + + + x + 2 + + + + + + + a + 3 + + + x + 3 + + + + … + + as the stream whose +elements are the coefficients + + a + 0 + +, + + a + 1 + +, + + a + 2 + +, + + a + 3 + +, …. + + + The integral of the series + + + a + 0 + + + + + a + 1 + + x + + + + + + a + 2 + + + x + 2 + + + + + + + a + 3 + + + x + 3 + + + + … + + is the series + + c + + + + + a + 0 + + x + + + + + + 1 + 2 + + + a + 1 + + + x + 2 + + + + + + + 1 + 3 + + + a + 2 + + + x + 3 + + + + + + + 1 + 4 + + + a + 3 + + + x + 4 + + + + … + , + + +where + c + is any constant. Define a procedure integrate-series that +takes as input a stream + + a + 0 + +, + + a + 1 + +, + + a + 2 + +, … representing a power +series and returns the stream + + a + 0 + +, + + + + 1 + 2 + + + + a + 1 + + +, + + + + 1 + 3 + + + + a + 2 + + +, … of +coefficients of the non-constant terms of the integral of the series. (Since +the result has no constant term, it doesn’t represent a power series; when we +use integrate-series, we will cons on the appropriate constant.) + + The function + + x + ↦ + + e + x + + + is its own derivative. This implies that + + + e + x + + and the integral of + + e + x + + are the same series, except for the +constant term, which is + + + e + 0 + + = + 1 + +. Accordingly, we can generate the series +for + + e + x + + as + + +(define exp-series + (cons-stream + 1 (integrate-series exp-series))) + +Show how to generate the series for sine and cosine, starting from the facts +that the derivative of sine is cosine and the derivative of cosine is the +negative of sine: + + +(define cosine-series + (cons-stream 1 ⟨??⟩)) + +(define sine-series + (cons-stream 0 ⟨??⟩)) + diff --git a/sicp/3_002e6 b/sicp/3_002e6 new file mode 100644 index 0000000..5772c8e --- /dev/null +++ b/sicp/3_002e6 @@ -0,0 +1,10 @@ + +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. diff --git a/sicp/3_002e60 b/sicp/3_002e60 new file mode 100644 index 0000000..e6a2fcb --- /dev/null +++ b/sicp/3_002e60 @@ -0,0 +1,31 @@ + +Exercise 3.60: With power series represented as +streams of coefficients as in Exercise 3.59, adding series is implemented +by add-streams. Complete the definition of the following procedure for +multiplying series: + + +(define (mul-series s1 s2) + (cons-stream ⟨??⟩ (add-streams ⟨??⟩ ⟨??⟩))) + +You can test your procedure by verifying that + + + sin + 2 + + ⁡ + x + + + + cos + 2 + + ⁡ + x + = + 1 + , + + +using the series from Exercise 3.59. diff --git a/sicp/3_002e61 b/sicp/3_002e61 new file mode 100644 index 0000000..beebd2b --- /dev/null +++ b/sicp/3_002e61 @@ -0,0 +1,146 @@ + +Exercise 3.61: Let + S + be a power series +(Exercise 3.59) whose constant term is 1. Suppose we want to find the +power series + + 1 + + / + + S + +, that is, the series + X + such that + + S + X + = + 1 + +. +Write + + S + = + 1 + + + + S + R + + + where + + S + R + + is the part of + S + after the +constant term. Then we can solve for + X + as follows: + + + + + S + ⋅ + X + + + = + + + 1 + , + + + + + ( + 1 + + + + S + R + + ) + ⋅ + X + + + = + + + 1 + , + + + + + X + + + + S + R + + ⋅ + X + + + = + + + 1 + , + + + + + X + + + = + + + 1 + − + + S + R + + ⋅ + X + . + + + + +In other words, + X + is the power series whose constant term is 1 and whose +higher-order terms are given by the negative of + + S + R + + times + X +. Use +this idea to write a procedure invert-unit-series that computes + + 1 + + / + + S + + +for a power series + S + with constant term 1. You will need to use +mul-series from Exercise 3.60. diff --git a/sicp/3_002e62 b/sicp/3_002e62 new file mode 100644 index 0000000..19a94c8 --- /dev/null +++ b/sicp/3_002e62 @@ -0,0 +1,8 @@ + +Exercise 3.62: Use the results of Exercise 3.60 +and Exercise 3.61 to define a procedure div-series that +divides two power series. Div-series should work for any two series, +provided that the denominator series begins with a nonzero constant term. (If +the denominator has a zero constant term, then div-series should signal +an error.) Show how to use div-series together with the result of +Exercise 3.59 to generate the power series for tangent. diff --git a/sicp/3_002e63 b/sicp/3_002e63 new file mode 100644 index 0000000..b652d38 --- /dev/null +++ b/sicp/3_002e63 @@ -0,0 +1,18 @@ + +Exercise 3.63: Louis Reasoner asks why the +sqrt-stream procedure was not written in the following more +straightforward way, without the local variable guesses: + + +(define (sqrt-stream x) + (cons-stream + 1.0 + (stream-map (lambda (guess) + (sqrt-improve guess x)) + (sqrt-stream x)))) + +Alyssa P. Hacker replies that this version of the procedure is considerably +less efficient because it performs redundant computation. Explain Alyssa’s +answer. Would the two versions still differ in efficiency if our +implementation of delay used only (lambda () ⟨exp⟩) without +using the optimization provided by memo-proc (3.5.1)? diff --git a/sicp/3_002e64 b/sicp/3_002e64 new file mode 100644 index 0000000..5a03dbc --- /dev/null +++ b/sicp/3_002e64 @@ -0,0 +1,11 @@ + +Exercise 3.64: Write a procedure +stream-limit that takes as arguments a stream and a number (the +tolerance). It should examine the stream until it finds two successive +elements that differ in absolute value by less than the tolerance, and return +the second of the two elements. Using this, we could compute square roots up +to a given tolerance by + + +(define (sqrt x tolerance) + (stream-limit (sqrt-stream x) tolerance)) diff --git a/sicp/3_002e65 b/sicp/3_002e65 new file mode 100644 index 0000000..2852eb3 --- /dev/null +++ b/sicp/3_002e65 @@ -0,0 +1,39 @@ + +Exercise 3.65: Use the series + + ln + ⁡ + 2 + + = + + 1 + − + + + 1 + 2 + + + + + + + 1 + 3 + + + − + + + 1 + 4 + + + + + … + +to compute three sequences of approximations to the natural logarithm of 2, in +the same way we did above for + π +. How rapidly do these sequences +converge? diff --git a/sicp/3_002e66 b/sicp/3_002e66 new file mode 100644 index 0000000..58bbbe2 --- /dev/null +++ b/sicp/3_002e66 @@ -0,0 +1,7 @@ + +Exercise 3.66: Examine the stream (pairs +integers integers). Can you make any general comments about the order in which +the pairs are placed into the stream? For example, approximately how many pairs precede +the pair (1, 100)? the pair (99, 100)? the pair (100, 100)? (If you can make +precise mathematical statements here, all the better. But feel free to give +more qualitative answers if you find yourself getting bogged down.) diff --git a/sicp/3_002e67 b/sicp/3_002e67 new file mode 100644 index 0000000..8c4e29e --- /dev/null +++ b/sicp/3_002e67 @@ -0,0 +1,19 @@ + +Exercise 3.67: Modify the pairs procedure +so that (pairs integers integers) will produce the stream of all +pairs of integers + + ( + i + , + j + ) + + (without the condition + + i + ≤ + j + +). Hint: +You will need to mix in an additional stream. diff --git a/sicp/3_002e68 b/sicp/3_002e68 new file mode 100644 index 0000000..cd5d639 --- /dev/null +++ b/sicp/3_002e68 @@ -0,0 +1,32 @@ + +Exercise 3.68: Louis Reasoner thinks that +building a stream of pairs from three parts is unnecessarily complicated. +Instead of separating the pair + + ( + + S + 0 + + , + + T + 0 + + ) + + from the rest of the pairs in +the first row, he proposes to work with the whole first row, as follows: + + +(define (pairs s t) + (interleave + (stream-map + (lambda (x) + (list (stream-car s) x)) + t) + (pairs (stream-cdr s) + (stream-cdr t)))) + +Does this work? Consider what happens if we evaluate (pairs integers +integers) using Louis’s definition of pairs. diff --git a/sicp/3_002e69 b/sicp/3_002e69 new file mode 100644 index 0000000..8f701f5 --- /dev/null +++ b/sicp/3_002e69 @@ -0,0 +1,73 @@ + +Exercise 3.69: Write a procedure triples +that takes three infinite streams, + S +, + T +, and + U +, and produces the +stream of triples + + ( + + S + i + + , + + T + j + + , + + U + k + + ) + + such that + + i + ≤ + j + ≤ + k + +. +Use triples to generate the stream of all Pythagorean +triples of positive integers, i.e., the triples + + ( + i + , + j + , + k + ) + + such that + + + i + ≤ + j + + and + + + i + 2 + + + + + j + 2 + + = + + k + 2 + + +. diff --git a/sicp/3_002e7 b/sicp/3_002e7 new file mode 100644 index 0000000..4a87308 --- /dev/null +++ b/sicp/3_002e7 @@ -0,0 +1,22 @@ + +Exercise 3.7: Consider the bank account objects +created by make-account, with the password modification described in +Exercise 3.3. Suppose that our banking system requires the ability to +make joint accounts. Define a procedure make-joint that accomplishes +this. Make-joint should take three arguments. The first is a +password-protected account. The second argument must match the password with +which the account was defined in order for the make-joint operation to +proceed. The third argument is a new password. Make-joint is to create +an additional access to the original account using the new password. For +example, if peter-acc is a bank account with password +open-sesame, then + + +(define paul-acc + (make-joint peter-acc + 'open-sesame + 'rosebud)) + +will allow one to make transactions on peter-acc using the name +paul-acc and the password rosebud. You may wish to modify your +solution to Exercise 3.3 to accommodate this new feature. diff --git a/sicp/3_002e70 b/sicp/3_002e70 new file mode 100644 index 0000000..6e7c8cb --- /dev/null +++ b/sicp/3_002e70 @@ -0,0 +1,144 @@ + +Exercise 3.70: It would be nice to be able to +generate streams in which the pairs appear in some useful order, rather than in +the order that results from an ad hoc interleaving process. We can use +a technique similar to the merge procedure of Exercise 3.56, if we +define a way to say that one pair of integers is “less than” another. One +way to do this is to define a “weighting function” + + W + ( + i + , + j + ) + + and +stipulate that + + ( + + i + 1 + + , + + j + 1 + + ) + + is less than + + ( + + i + 2 + + , + + j + 2 + + ) + + if + + + W + ( + + i + 1 + + , + + j + 1 + + ) + < + + + W + ( + + i + 2 + + , + + j + 2 + + ) + +. Write a procedure +merge-weighted that is like merge, except that +merge-weighted takes an additional argument weight, which is a +procedure that computes the weight of a pair, and is used to determine the +order in which elements should appear in the resulting merged +stream.197 Using this, generalize pairs to a procedure +weighted-pairs that takes two streams, together with a procedure that +computes a weighting function, and generates the stream of pairs, ordered +according to weight. Use your procedure to generate + + + the stream of all pairs of positive integers + + ( + i + , + j + ) + + with + + i + ≤ + j + + +ordered according to the sum + + i + + + j + +, + + the stream of all pairs of positive integers + + ( + i + , + j + ) + + with + + i + ≤ + j + +, +where neither + i + nor + j + is divisible by 2, 3, or 5, and the pairs are +ordered according to the sum + + 2 + i + + + 3 + j + + + 5 + i + j + +. + + diff --git a/sicp/3_002e71 b/sicp/3_002e71 new file mode 100644 index 0000000..7e4001e --- /dev/null +++ b/sicp/3_002e71 @@ -0,0 +1,32 @@ + +Exercise 3.71: Numbers that can be expressed as +the sum of two cubes in more than one way are sometimes called + +Ramanujan numbers, in honor of the mathematician Srinivasa +Ramanujan.198 Ordered streams of pairs provide an elegant +solution to the problem of computing these numbers. To find a number that can +be written as the sum of two cubes in two different ways, we need only generate +the stream of pairs of integers + + ( + i + , + j + ) + + weighted according to the sum + + + + i + 3 + + + + + j + 3 + + + (see Exercise 3.70), then search the stream for two +consecutive pairs with the same weight. Write a procedure to generate the +Ramanujan numbers. The first such number is 1,729. What are the next five? diff --git a/sicp/3_002e72 b/sicp/3_002e72 new file mode 100644 index 0000000..934c324 --- /dev/null +++ b/sicp/3_002e72 @@ -0,0 +1,4 @@ + +Exercise 3.72: In a similar way to Exercise 3.71 +generate a stream of all numbers that can be written as the sum of two +squares in three different ways (showing how they can be so written). diff --git a/sicp/3_002e73 b/sicp/3_002e73 new file mode 100644 index 0000000..2b28b66 --- /dev/null +++ b/sicp/3_002e73 @@ -0,0 +1,58 @@ + +Exercise 3.73: We can model electrical circuits +using streams to represent the values of currents or voltages at a sequence of +times. For instance, suppose we have an +RC circuit consisting of a +resistor of resistance + R + and a capacitor of capacitance + C + in series. +The voltage response + v + of the circuit to an injected current + i + is +determined by the formula in Figure 3.33, whose structure is shown by the +accompanying signal-flow diagram. + + + +SVG + + +Figure 3.33: An RC circuit and the associated signal-flow diagram. + + + +Write a procedure RC that models this circuit. RC should take as +inputs the values of + R +, + C +, and + + d + t + + and should return a procedure +that takes as inputs a stream representing the current + i + and an initial +value for the capacitor voltage + + v + 0 + + and produces as output the stream of +voltages + v +. For example, you should be able to use RC to model an +RC circuit with + R + = 5 ohms, + C + = 1 farad, and a 0.5-second time step by +evaluating (define RC1 (RC 5 1 0.5)). This defines RC1 as a +procedure that takes a stream representing the time sequence of currents and an +initial capacitor voltage and produces the output stream of voltages. diff --git a/sicp/3_002e74 b/sicp/3_002e74 new file mode 100644 index 0000000..fff9b7a --- /dev/null +++ b/sicp/3_002e74 @@ -0,0 +1,71 @@ + +Exercise 3.74: Alyssa P. Hacker is designing a +system to process signals coming from physical sensors. One important feature +she wishes to produce is a signal that describes the +zero crossings +of the input signal. That is, the resulting signal should be + + + + 1 + + whenever the +input signal changes from negative to positive, + + − + 1 + + whenever the input signal +changes from positive to negative, and + 0 + otherwise. (Assume that the sign of a + + 0 + input is positive.) For example, a typical input signal with its associated +zero-crossing signal would be + + +… 1 2 1.5 1 0.5 -0.1 -2 -3 -2 -0.5 0.2 3 4 … +… 0 0 0 0 0 -1 0 0 0 0 1 0 0 … + + +In Alyssa’s system, the signal from the sensor is represented as a stream +sense-data and the stream zero-crossings is the corresponding +stream of zero crossings. Alyssa first writes a procedure +sign-change-detector that takes two values as arguments and compares the +signs of the values to produce an appropriate + 0 +, + 1 +, or + + − + 1 + +. She then +constructs her zero-crossing stream as follows: + + +(define (make-zero-crossings + input-stream last-value) + (cons-stream + (sign-change-detector + (stream-car input-stream) + last-value) + (make-zero-crossings + (stream-cdr input-stream) + (stream-car input-stream)))) + +(define zero-crossings + (make-zero-crossings sense-data 0)) + +Alyssa’s boss, Eva Lu Ator, walks by and suggests that this program is +approximately equivalent to the following one, which uses the generalized +version of stream-map from Exercise 3.50: + + +(define zero-crossings + (stream-map sign-change-detector + sense-data + ⟨expression⟩)) + +Complete the program by supplying the indicated ⟨expression⟩. diff --git a/sicp/3_002e75 b/sicp/3_002e75 new file mode 100644 index 0000000..a087414 --- /dev/null +++ b/sicp/3_002e75 @@ -0,0 +1,26 @@ + +Exercise 3.75: Unfortunately, Alyssa’s +zero-crossing detector in Exercise 3.74 proves to be insufficient, +because the noisy signal from the sensor leads to spurious zero crossings. Lem +E. Tweakit, a hardware specialist, suggests that Alyssa smooth the signal to +filter out the noise before extracting the zero crossings. Alyssa takes his +advice and decides to extract the zero crossings from the signal constructed by +averaging each value of the sense data with the previous value. She explains +the problem to her assistant, Louis Reasoner, who attempts to implement the +idea, altering Alyssa’s program as follows: + + +(define (make-zero-crossings + input-stream last-value) + (let ((avpt + (/ (+ (stream-car input-stream) + last-value) + 2))) + (cons-stream + (sign-change-detector avpt last-value) + (make-zero-crossings + (stream-cdr input-stream) avpt)))) + +This does not correctly implement Alyssa’s plan. Find the bug that Louis has +installed and fix it without changing the structure of the program. (Hint: You +will need to increase the number of arguments to make-zero-crossings.) diff --git a/sicp/3_002e76 b/sicp/3_002e76 new file mode 100644 index 0000000..76da76f --- /dev/null +++ b/sicp/3_002e76 @@ -0,0 +1,10 @@ + +Exercise 3.76: Eva Lu Ator has a criticism of +Louis’s approach in Exercise 3.75. The program he wrote is not modular, +because it intermixes the operation of smoothing with the zero-crossing +extraction. For example, the extractor should not have to be changed if Alyssa +finds a better way to condition her input signal. Help Louis by writing a +procedure smooth that takes a stream as input and produces a stream in +which each element is the average of two successive input stream elements. +Then use smooth as a component to implement the zero-crossing detector +in a more modular style. diff --git a/sicp/3_002e77 b/sicp/3_002e77 new file mode 100644 index 0000000..73d0cff --- /dev/null +++ b/sicp/3_002e77 @@ -0,0 +1,24 @@ + +Exercise 3.77: The integral procedure +used above was analogous to the “implicit” definition of the infinite stream +of integers in 3.5.2. Alternatively, we can give a definition of +integral that is more like integers-starting-from (also in +3.5.2): + + +(define (integral + integrand initial-value dt) + (cons-stream + initial-value + (if (stream-null? integrand) + the-empty-stream + (integral + (stream-cdr integrand) + (+ (* dt (stream-car integrand)) + initial-value) + dt)))) + +When used in systems with loops, this procedure has the same problem as does +our original version of integral. Modify the procedure so that it +expects the integrand as a delayed argument and hence can be used in the +solve procedure shown above. diff --git a/sicp/3_002e78 b/sicp/3_002e78 new file mode 100644 index 0000000..c56e6d5 --- /dev/null +++ b/sicp/3_002e78 @@ -0,0 +1,137 @@ + +Exercise 3.78: Consider the problem of designing +a signal-processing system to study the homogeneous second-order linear +differential equation + + + + + d + 2 + + y + + + d + + t + 2 + + + + − + + a + + + d + y + + + d + t + + + + − + + b + y + + + = + + + 0. + + +The output stream, modeling + y +, is generated by a network that contains a +loop. This is because the value of + + + d + 2 + + y + + / + + d + + t + 2 + + + depends upon the +values of + y + and + + d + y + + / + + d + t + + and both of these are determined by +integrating + + + d + 2 + + y + + / + + d + + t + 2 + + +. The diagram we would like to encode is +shown in Figure 3.35. Write a procedure solve-2nd that takes as +arguments the constants + a +, + b +, and + + d + t + + and the initial values + + + y + 0 + + and + + d + + y + 0 + + + for + y + and + + d + y + + / + + d + t + + and generates the +stream of successive values of + y +. diff --git a/sicp/3_002e79 b/sicp/3_002e79 new file mode 100644 index 0000000..9ef0cc9 --- /dev/null +++ b/sicp/3_002e79 @@ -0,0 +1,35 @@ + +Exercise 3.79: Generalize the solve-2nd +procedure of Exercise 3.78 so that it can be used to solve general +second-order differential equations + + + d + 2 + + y + + / + + d + + t + 2 + + + = + + f + ( + d + y + + / + + d + t + , + y + ) + +. diff --git a/sicp/3_002e8 b/sicp/3_002e8 new file mode 100644 index 0000000..90635ee --- /dev/null +++ b/sicp/3_002e8 @@ -0,0 +1,15 @@ + +Exercise 3.8: When we defined the evaluation +model in 1.1.3, we said that the first step in evaluating an +expression is to evaluate its subexpressions. But we never specified the order +in which the subexpressions should be evaluated (e.g., left to right or right +to left). When we introduce assignment, the order in which the arguments to a +procedure are evaluated can make a difference to the result. Define a simple +procedure f such that evaluating + + +(+ (f 0) (f 1)) + +will return 0 if +the arguments to + are evaluated from left to right but will return 1 if +the arguments are evaluated from right to left. diff --git a/sicp/3_002e80 b/sicp/3_002e80 new file mode 100644 index 0000000..d8517e7 --- /dev/null +++ b/sicp/3_002e80 @@ -0,0 +1,265 @@ + +Exercise 3.80: A +series RLC circuit +consists of a resistor, a capacitor, and an inductor connected in series, as +shown in Figure 3.36. If + R +, + L +, and + C + are the resistance, +inductance, and capacitance, then the relations between voltage + + ( + v + ) + + and +current + + ( + i + ) + + for the three components are described by the equations + + + + + + v + R + + + + = + + + + i + R + + R + , + + + + + + v + L + + + + = + + + L + + + + + d + + i + L + + + + d + t + + + + , + + + + + + i + C + + + + = + + + C + + + + + d + + v + C + + + + d + t + + + + , + + + + +and the circuit connections dictate the relations + + + + + + i + R + + + + = + + + + i + L + + = + − + + i + C + + , + + + + + + v + C + + + + = + + + + v + L + + + + + v + R + + . + + + + +Combining these equations shows that the state of the circuit (summarized by + + + v + C + +, the voltage across the capacitor, and + + i + L + +, the current in +the inductor) is described by the pair of differential equations + + + + + + + + d + + v + C + + + + d + t + + + + + + = + + + − + + + + i + L + + C + + + + , + + + + + + + + d + + i + L + + + + d + t + + + + + + = + + + + + 1 + L + + + + + v + C + + − + + + R + L + + + + + i + L + + . + + + + +The signal-flow diagram representing this system of differential equations is +shown in Figure 3.37. diff --git a/sicp/3_002e81 b/sicp/3_002e81 new file mode 100644 index 0000000..51f5c52 --- /dev/null +++ b/sicp/3_002e81 @@ -0,0 +1,8 @@ + +Exercise 3.81: Exercise 3.6 discussed +generalizing the random-number generator to allow one to reset the +random-number sequence so as to produce repeatable sequences of “random” +numbers. Produce a stream formulation of this same generator that operates on +an input stream of requests to generate a new random number or to +reset the sequence to a specified value and that produces the desired +stream of random numbers. Don’t use assignment in your solution. diff --git a/sicp/3_002e82 b/sicp/3_002e82 new file mode 100644 index 0000000..f5cc27d --- /dev/null +++ b/sicp/3_002e82 @@ -0,0 +1,6 @@ + +Exercise 3.82: Redo Exercise 3.5 on Monte +Carlo integration in terms of streams. The stream version of +estimate-integral will not have an argument telling how many trials to +perform. Instead, it will produce a stream of estimates based on successively +more trials. diff --git a/sicp/3_002e9 b/sicp/3_002e9 new file mode 100644 index 0000000..7d3f5b3 --- /dev/null +++ b/sicp/3_002e9 @@ -0,0 +1,28 @@ + +Exercise 3.9: In 1.2.1 we used the +substitution model to analyze two procedures for computing factorials, a +recursive version + + +(define (factorial n) + (if (= n 1) + 1 + (* n (factorial (- n 1))))) + +and an iterative version + + +(define (factorial n) + (fact-iter 1 1 n)) + +(define (fact-iter product + counter + max-count) + (if (> counter max-count) + product + (fact-iter (* counter product) + (+ counter 1) + max-count))) + +Show the environment structures created by evaluating (factorial 6) +using each version of the factorial procedure.142 diff --git a/sicp/4_002e1 b/sicp/4_002e1 new file mode 100644 index 0000000..7408337 --- /dev/null +++ b/sicp/4_002e1 @@ -0,0 +1,12 @@ + +Exercise 4.1: Notice that we cannot tell whether +the metacircular evaluator evaluates operands from left to right or from right +to left. Its evaluation order is inherited from the underlying Lisp: If the +arguments to cons in list-of-values are evaluated from left to +right, then list-of-values will evaluate operands from left to right; +and if the arguments to cons are evaluated from right to left, then +list-of-values will evaluate operands from right to left. + +Write a version of list-of-values that evaluates operands from left to +right regardless of the order of evaluation in the underlying Lisp. Also write +a version of list-of-values that evaluates operands from right to left. diff --git a/sicp/4_002e10 b/sicp/4_002e10 new file mode 100644 index 0000000..399d118 --- /dev/null +++ b/sicp/4_002e10 @@ -0,0 +1,6 @@ + +Exercise 4.10: By using data abstraction, we +were able to write an eval procedure that is independent of the +particular syntax of the language to be evaluated. To illustrate this, design +and implement a new syntax for Scheme by modifying the procedures in this +section, without changing eval or apply. diff --git a/sicp/4_002e11 b/sicp/4_002e11 new file mode 100644 index 0000000..286666a --- /dev/null +++ b/sicp/4_002e11 @@ -0,0 +1,5 @@ + +Exercise 4.11: Instead of representing a frame +as a pair of lists, we can represent a frame as a list of bindings, where each +binding is a name-value pair. Rewrite the environment operations to use this +alternative representation. diff --git a/sicp/4_002e12 b/sicp/4_002e12 new file mode 100644 index 0000000..a9b38e4 --- /dev/null +++ b/sicp/4_002e12 @@ -0,0 +1,7 @@ + +Exercise 4.12: The procedures +define-variable!, set-variable-value! and +lookup-variable-value can be expressed in terms of more abstract +procedures for traversing the environment structure. Define abstractions that +capture the common patterns and redefine the three procedures in terms of these +abstractions. diff --git a/sicp/4_002e13 b/sicp/4_002e13 new file mode 100644 index 0000000..f5efa9a --- /dev/null +++ b/sicp/4_002e13 @@ -0,0 +1,9 @@ + +Exercise 4.13: Scheme allows us to create new +bindings for variables by means of define, but provides no way to get +rid of bindings. Implement for the evaluator a special form +make-unbound! that removes the binding of a given symbol from the +environment in which the make-unbound! expression is evaluated. This +problem is not completely specified. For example, should we remove only the +binding in the first frame of the environment? Complete the specification and +justify any choices you make. diff --git a/sicp/4_002e14 b/sicp/4_002e14 new file mode 100644 index 0000000..ddc38c7 --- /dev/null +++ b/sicp/4_002e14 @@ -0,0 +1,7 @@ + +Exercise 4.14: Eva Lu Ator and Louis Reasoner +are each experimenting with the metacircular evaluator. Eva types in the +definition of map, and runs some test programs that use it. They work +fine. Louis, in contrast, has installed the system version of map as a +primitive for the metacircular evaluator. When he tries it, things go terribly +wrong. Explain why Louis’s map fails even though Eva’s works. diff --git a/sicp/4_002e15 b/sicp/4_002e15 new file mode 100644 index 0000000..d1cdc40 --- /dev/null +++ b/sicp/4_002e15 @@ -0,0 +1,22 @@ + +Exercise 4.15: Given a one-argument procedure +p and an object a, p is said to “halt” on a if +evaluating the expression (p a) returns a value (as opposed to +terminating with an error message or running forever). Show that it is +impossible to write a procedure halts? that correctly determines whether +p halts on a for any procedure p and object a. Use +the following reasoning: If you had such a procedure halts?, you could +implement the following program: + + +(define (run-forever) + (run-forever)) + +(define (try p) + (if (halts? p p) + (run-forever) + 'halted)) + +Now consider evaluating the expression (try try) and show that any +possible outcome (either halting or running forever) violates the intended +behavior of halts?.227 diff --git a/sicp/4_002e16 b/sicp/4_002e16 new file mode 100644 index 0000000..18b0cf5 --- /dev/null +++ b/sicp/4_002e16 @@ -0,0 +1,18 @@ + +Exercise 4.16: In this exercise we implement the +method just described for interpreting internal definitions. We assume that +the evaluator supports let (see Exercise 4.6). + + + Change lookup-variable-value (4.1.3) to signal an error if +the value it finds is the symbol *unassigned*. + + Write a procedure scan-out-defines that takes a procedure body and +returns an equivalent one that has no internal definitions, by making the +transformation described above. + + Install scan-out-defines in the interpreter, either in +make-procedure or in procedure-body (see 4.1.3). +Which place is better? Why? + + diff --git a/sicp/4_002e17 b/sicp/4_002e17 new file mode 100644 index 0000000..e4623a0 --- /dev/null +++ b/sicp/4_002e17 @@ -0,0 +1,10 @@ + +Exercise 4.17: Draw diagrams of the environment +in effect when evaluating the expression ⟨e3⟩ in the procedure in the +text, comparing how this will be structured when definitions are interpreted +sequentially with how it will be structured if definitions are scanned out as +described. Why is there an extra frame in the transformed program? Explain +why this difference in environment structure can never make a difference in the +behavior of a correct program. Design a way to make the interpreter implement +the “simultaneous” scope rule for internal definitions without constructing +the extra frame. diff --git a/sicp/4_002e18 b/sicp/4_002e18 new file mode 100644 index 0000000..c0ebe6d --- /dev/null +++ b/sicp/4_002e18 @@ -0,0 +1,26 @@ + +Exercise 4.18: Consider an alternative strategy +for scanning out definitions that translates the example in the text to + + +(lambda ⟨vars⟩ + (let ((u '*unassigned*) + (v '*unassigned*)) + (let ((a ⟨e1⟩) + (b ⟨e2⟩)) + (set! u a) + (set! v b)) + ⟨e3⟩)) + +Here a and b are meant to represent new variable names, created +by the interpreter, that do not appear in the user’s program. Consider the +solve procedure from 3.5.4: + + +(define (solve f y0 dt) + (define y (integral (delay dy) y0 dt)) + (define dy (stream-map f y)) + y) + +Will this procedure work if internal definitions are scanned out as shown in +this exercise? What if they are scanned out as shown in the text? Explain. diff --git a/sicp/4_002e19 b/sicp/4_002e19 new file mode 100644 index 0000000..9d58c9d --- /dev/null +++ b/sicp/4_002e19 @@ -0,0 +1,27 @@ + +Exercise 4.19: Ben Bitdiddle, Alyssa P. Hacker, +and Eva Lu Ator are arguing about the desired result of evaluating the +expression + + +(let ((a 1)) + (define (f x) + (define b (+ a x)) + (define a 5) + (+ a b)) + (f 10)) + +Ben asserts that the result should be obtained using the sequential rule for +define: b is defined to be 11, then a is defined to be 5, +so the result is 16. Alyssa objects that mutual recursion requires the +simultaneous scope rule for internal procedure definitions, and that it is +unreasonable to treat procedure names differently from other names. Thus, she +argues for the mechanism implemented in Exercise 4.16. This would lead +to a being unassigned at the time that the value for b is to be +computed. Hence, in Alyssa’s view the procedure should produce an error. Eva +has a third opinion. She says that if the definitions of a and b +are truly meant to be simultaneous, then the value 5 for a should be +used in evaluating b. Hence, in Eva’s view a should be 5, +b should be 15, and the result should be 20. Which (if any) of these +viewpoints do you support? Can you devise a way to implement internal +definitions so that they behave as Eva prefers?230 diff --git a/sicp/4_002e2 b/sicp/4_002e2 new file mode 100644 index 0000000..2c2c711 --- /dev/null +++ b/sicp/4_002e2 @@ -0,0 +1,22 @@ + +Exercise 4.2: Louis Reasoner plans to reorder the +cond clauses in eval so that the clause for procedure +applications appears before the clause for assignments. He argues that this +will make the interpreter more efficient: Since programs usually contain more +applications than assignments, definitions, and so on, his modified eval +will usually check fewer clauses than the original eval before +identifying the type of an expression. + + + What is wrong with Louis’s plan? (Hint: What will Louis’s evaluator do with +the expression (define x 3)?) + + Louis is upset that his plan didn’t work. He is willing to go to any lengths +to make his evaluator recognize procedure applications before it checks for +most other kinds of expressions. Help him by changing the syntax of the +evaluated language so that procedure applications start with call. For +example, instead of (factorial 3) we will now have to write (call +factorial 3) and instead of (+ 1 2) we will have to write (call + +1 2). + + diff --git a/sicp/4_002e20 b/sicp/4_002e20 new file mode 100644 index 0000000..931250e --- /dev/null +++ b/sicp/4_002e20 @@ -0,0 +1,87 @@ + +Exercise 4.20: Because internal definitions look +sequential but are actually simultaneous, some people prefer to avoid them +entirely, and use the special form letrec instead. Letrec looks +like let, so it is not surprising that the variables it binds are bound +simultaneously and have the same scope as each other. The sample procedure +f above can be written without internal definitions, but with exactly +the same meaning, as + + +(define (f x) + (letrec + ((even? + (lambda (n) + (if (= n 0) + true + (odd? (- n 1))))) + (odd? + (lambda (n) + (if (= n 0) + false + (even? (- n 1)))))) + ⟨rest of body of f⟩)) + +Letrec expressions, which have the form + + +(letrec ((⟨var₁⟩ ⟨exp₁⟩) … (⟨varₙ⟩ ⟨expₙ⟩)) + ⟨body⟩) + +are a variation on let in which the expressions + + + ⟨ + + e + x + + p + k + + ⟩ + + that provide the initial values for the +variables + + ⟨ + + v + a + + r + k + + ⟩ + + are evaluated in an environment +that includes all the letrec bindings. This permits recursion in the +bindings, such as the mutual recursion of even? and odd? in the +example above, or the evaluation of 10 factorial with + + +(letrec + ((fact + (lambda (n) + (if (= n 1) + 1 + (* n (fact (- n 1))))))) + (fact 10)) + + + Implement letrec as a derived expression, by transforming a +letrec expression into a let expression as shown in the text +above or in Exercise 4.18. That is, the letrec variables should +be created with a let and then be assigned their values with +set!. + + Louis Reasoner is confused by all this fuss about internal definitions. The +way he sees it, if you don’t like to use define inside a procedure, you +can just use let. Illustrate what is loose about his reasoning by +drawing an environment diagram that shows the environment in which the +⟨rest of body of f⟩ is evaluated during evaluation of the +expression (f 5), with f defined as in this exercise. Draw an +environment diagram for the same evaluation, but with let in place of +letrec in the definition of f. + + diff --git a/sicp/4_002e21 b/sicp/4_002e21 new file mode 100644 index 0000000..04c5c9c --- /dev/null +++ b/sicp/4_002e21 @@ -0,0 +1,52 @@ + +Exercise 4.21: Amazingly, Louis’s intuition in +Exercise 4.20 is correct. It is indeed possible to specify recursive +procedures without using letrec (or even define), although the +method for accomplishing this is much more subtle than Louis imagined. The +following expression computes 10 factorial by applying a recursive factorial +procedure:231 + + +((lambda (n) + ((lambda (fact) (fact fact n)) + (lambda (ft k) + (if (= k 1) + 1 + (* k (ft ft (- k 1))))))) + 10) + + + Check (by evaluating the expression) that this really does compute factorials. +Devise an analogous expression for computing Fibonacci numbers. + + Consider the following procedure, which includes mutually recursive internal +definitions: + + +(define (f x) + (define (even? n) + (if (= n 0) + true + (odd? (- n 1)))) + (define (odd? n) + (if (= n 0) + false + (even? (- n 1)))) + (even? x)) + +Fill in the missing expressions to complete an alternative definition of +f, which uses neither internal definitions nor letrec: + + +(define (f x) + ((lambda (even? odd?) + (even? even? odd? x)) + (lambda (ev? od? n) + (if (= n 0) + true + (od? ⟨??⟩ ⟨??⟩ ⟨??⟩))) + (lambda (ev? od? n) + (if (= n 0) + false + (ev? ⟨??⟩ ⟨??⟩ ⟨??⟩))))) + diff --git a/sicp/4_002e22 b/sicp/4_002e22 new file mode 100644 index 0000000..b84d744 --- /dev/null +++ b/sicp/4_002e22 @@ -0,0 +1,3 @@ + +Exercise 4.22: Extend the evaluator in this +section to support the special form let. (See Exercise 4.6.) diff --git a/sicp/4_002e23 b/sicp/4_002e23 new file mode 100644 index 0000000..123e065 --- /dev/null +++ b/sicp/4_002e23 @@ -0,0 +1,35 @@ + +Exercise 4.23: Alyssa P. Hacker doesn’t +understand why analyze-sequence needs to be so complicated. All the +other analysis procedures are straightforward transformations of the +corresponding evaluation procedures (or eval clauses) in +4.1.1. She expected analyze-sequence to look like this: + + +(define (analyze-sequence exps) + (define (execute-sequence procs env) + (cond ((null? (cdr procs)) + ((car procs) env)) + (else ((car procs) env) + (execute-sequence + (cdr procs) env)))) + (let ((procs (map analyze exps))) + (if (null? procs) + (error "Empty sequence: + ANALYZE")) + (lambda (env) + (execute-sequence procs env)))) + +Eva Lu Ator explains to Alyssa that the version in the text does more of the +work of evaluating a sequence at analysis time. Alyssa’s sequence-execution +procedure, rather than having the calls to the individual execution procedures +built in, loops through the procedures in order to call them: In effect, +although the individual expressions in the sequence have been analyzed, the +sequence itself has not been. + +Compare the two versions of analyze-sequence. For example, consider the +common case (typical of procedure bodies) where the sequence has just one +expression. What work will the execution procedure produced by Alyssa’s +program do? What about the execution procedure produced by the program in the +text above? How do the two versions compare for a sequence with two +expressions? diff --git a/sicp/4_002e24 b/sicp/4_002e24 new file mode 100644 index 0000000..17045d4 --- /dev/null +++ b/sicp/4_002e24 @@ -0,0 +1,5 @@ + +Exercise 4.24: Design and carry out some +experiments to compare the speed of the original metacircular evaluator with +the version in this section. Use your results to estimate the fraction of time +that is spent in analysis versus execution for various procedures. diff --git a/sicp/4_002e25 b/sicp/4_002e25 new file mode 100644 index 0000000..dd92959 --- /dev/null +++ b/sicp/4_002e25 @@ -0,0 +1,13 @@ + +Exercise 4.25: Suppose that (in ordinary +applicative-order Scheme) we define unless as shown above and then +define factorial in terms of unless as + + +(define (factorial n) + (unless (= n 1) + (* n (factorial (- n 1))) + 1)) + +What happens if we attempt to evaluate (factorial 5)? Will our +definitions work in a normal-order language? diff --git a/sicp/4_002e26 b/sicp/4_002e26 new file mode 100644 index 0000000..f7811cf --- /dev/null +++ b/sicp/4_002e26 @@ -0,0 +1,11 @@ + +Exercise 4.26: Ben Bitdiddle and Alyssa +P. Hacker disagree over the importance of lazy evaluation for implementing +things such as unless. Ben points out that it’s possible to implement +unless in applicative order as a special form. Alyssa counters that, if +one did that, unless would be merely syntax, not a procedure that could +be used in conjunction with higher-order procedures. Fill in the details on +both sides of the argument. Show how to implement unless as a derived +expression (like cond or let), and give an example of a situation +where it might be useful to have unless available as a procedure, rather +than as a special form. diff --git a/sicp/4_002e27 b/sicp/4_002e27 new file mode 100644 index 0000000..7a2a97a --- /dev/null +++ b/sicp/4_002e27 @@ -0,0 +1,31 @@ + +Exercise 4.27: Suppose we type in the following +definitions to the lazy evaluator: + + +(define count 0) +(define (id x) (set! count (+ count 1)) x) + +Give the missing values in the following sequence of interactions, and explain +your answers.242 + + +(define w (id (id 10))) + +;;; L-Eval input: +count + +;;; L-Eval value: +⟨response⟩ + +;;; L-Eval input: +w + +;;; L-Eval value: +⟨response⟩ + +;;; L-Eval input: +count + +;;; L-Eval value: +⟨response⟩ diff --git a/sicp/4_002e28 b/sicp/4_002e28 new file mode 100644 index 0000000..934d543 --- /dev/null +++ b/sicp/4_002e28 @@ -0,0 +1,27 @@ + +Exercise 4.28: Eval uses +actual-value rather than eval to evaluate the operator before +passing it to apply, in order to force the value of the operator. Give +an example that demonstrates the need for this forcing. + +Exercise 4.29: Exhibit a program that you would +expect to run much more slowly without memoization than with memoization. +Also, consider the following interaction, where the id procedure is +defined as in Exercise 4.27 and count starts at 0: + + +(define (square x) (* x x)) + +;;; L-Eval input: +(square (id 10)) + +;;; L-Eval value: +⟨response⟩ + +;;; L-Eval input: +count + +;;; L-Eval value: +⟨response⟩ + +Give the responses both when the evaluator memoizes and when it does not. diff --git a/sicp/4_002e29 b/sicp/4_002e29 new file mode 100644 index 0000000..934d543 --- /dev/null +++ b/sicp/4_002e29 @@ -0,0 +1,27 @@ + +Exercise 4.28: Eval uses +actual-value rather than eval to evaluate the operator before +passing it to apply, in order to force the value of the operator. Give +an example that demonstrates the need for this forcing. + +Exercise 4.29: Exhibit a program that you would +expect to run much more slowly without memoization than with memoization. +Also, consider the following interaction, where the id procedure is +defined as in Exercise 4.27 and count starts at 0: + + +(define (square x) (* x x)) + +;;; L-Eval input: +(square (id 10)) + +;;; L-Eval value: +⟨response⟩ + +;;; L-Eval input: +count + +;;; L-Eval value: +⟨response⟩ + +Give the responses both when the evaluator memoizes and when it does not. diff --git a/sicp/4_002e3 b/sicp/4_002e3 new file mode 100644 index 0000000..c7b9ef8 --- /dev/null +++ b/sicp/4_002e3 @@ -0,0 +1,6 @@ + +Exercise 4.3: Rewrite eval so that the +dispatch is done in data-directed style. Compare this with the data-directed +differentiation procedure of Exercise 2.73. (You may use the car +of a compound expression as the type of the expression, as is appropriate for +the syntax implemented in this section.) diff --git a/sicp/4_002e30 b/sicp/4_002e30 new file mode 100644 index 0000000..b626145 --- /dev/null +++ b/sicp/4_002e30 @@ -0,0 +1,77 @@ + +Exercise 4.30: Cy D. Fect, a reformed C +programmer, is worried that some side effects may never take place, because the +lazy evaluator doesn’t force the expressions in a sequence. Since the value of +an expression in a sequence other than the last one is not used (the expression +is there only for its effect, such as assigning to a variable or printing), +there can be no subsequent use of this value (e.g., as an argument to a +primitive procedure) that will cause it to be forced. Cy thus thinks that when +evaluating sequences, we must force all expressions in the sequence except the +final one. He proposes to modify eval-sequence from 4.1.1 +to use actual-value rather than eval: + + +(define (eval-sequence exps env) + (cond ((last-exp? exps) + (eval (first-exp exps) env)) + (else + (actual-value (first-exp exps) + env) + (eval-sequence (rest-exps exps) + env)))) + + + Ben Bitdiddle thinks Cy is wrong. He shows Cy the for-each procedure +described in Exercise 2.23, which gives an important example of a +sequence with side effects: + + +(define (for-each proc items) + (if (null? items) + 'done + (begin (proc (car items)) + (for-each proc + (cdr items))))) + +He claims that the evaluator in the text (with the original +eval-sequence) handles this correctly: + + +;;; L-Eval input: +(for-each + (lambda (x) (newline) (display x)) + (list 57 321 88)) +57 +321 +88 + +;;; L-Eval value: +done + + +Explain why Ben is right about the behavior of for-each. + + Cy agrees that Ben is right about the for-each example, but says that +that’s not the kind of program he was thinking about when he proposed his +change to eval-sequence. He defines the following two procedures in the +lazy evaluator: + + +(define (p1 x) + (set! x (cons x '(2))) x) + +(define (p2 x) + (define (p e) e x) + (p (set! x (cons x '(2))))) + +What are the values of (p1 1) and (p2 1) with the original +eval-sequence? What would the values be with Cy’s proposed change to +eval-sequence? + + Cy also points out that changing eval-sequence as he proposes does not +affect the behavior of the example in part a. Explain why this is true. + + How do you think sequences ought to be treated in the lazy evaluator? Do you +like Cy’s approach, the approach in the text, or some other approach? + + diff --git a/sicp/4_002e31 b/sicp/4_002e31 new file mode 100644 index 0000000..1031d39 --- /dev/null +++ b/sicp/4_002e31 @@ -0,0 +1,27 @@ + +Exercise 4.31: The approach taken in this +section is somewhat unpleasant, because it makes an incompatible change to +Scheme. It might be nicer to implement lazy evaluation as an + +upward-compatible extension, that is, so that ordinary Scheme +programs will work as before. We can do this by extending the syntax of +procedure declarations to let the user control whether or not arguments are to +be delayed. While we’re at it, we may as well also give the user the choice +between delaying with and without memoization. For example, the definition + + +(define (f a (b lazy) c (d lazy-memo)) + …) + +would define f to be a procedure of four arguments, where the first and +third arguments are evaluated when the procedure is called, the second argument +is delayed, and the fourth argument is both delayed and memoized. Thus, +ordinary procedure definitions will produce the same behavior as ordinary +Scheme, while adding the lazy-memo declaration to each parameter of +every compound procedure will produce the behavior of the lazy evaluator +defined in this section. Design and implement the changes required to produce +such an extension to Scheme. You will have to implement new syntax procedures +to handle the new syntax for define. You must also arrange for +eval or apply to determine when arguments are to be delayed, and +to force or delay arguments accordingly, and you must arrange for forcing to +memoize or not, as appropriate. diff --git a/sicp/4_002e32 b/sicp/4_002e32 new file mode 100644 index 0000000..559e77a --- /dev/null +++ b/sicp/4_002e32 @@ -0,0 +1,5 @@ + +Exercise 4.32: Give some examples that +illustrate the difference between the streams of Chapter 3 and the +“lazier” lazy lists described in this section. How can you take advantage of +this extra laziness? diff --git a/sicp/4_002e33 b/sicp/4_002e33 new file mode 100644 index 0000000..99ca147 --- /dev/null +++ b/sicp/4_002e33 @@ -0,0 +1,12 @@ + +Exercise 4.33: Ben Bitdiddle tests the lazy list +implementation given above by evaluating the expression + + +(car '(a b c)) + +To his surprise, this produces an error. After some thought, he realizes that +the “lists” obtained by reading in quoted expressions are different from the +lists manipulated by the new definitions of cons, car, and +cdr. Modify the evaluator’s treatment of quoted expressions so that +quoted lists typed at the driver loop will produce true lazy lists. diff --git a/sicp/4_002e34 b/sicp/4_002e34 new file mode 100644 index 0000000..2f3a444 --- /dev/null +++ b/sicp/4_002e34 @@ -0,0 +1,6 @@ + +Exercise 4.34: Modify the driver loop for the +evaluator so that lazy pairs and lists will print in some reasonable way. +(What are you going to do about infinite lists?) You may also need to modify +the representation of lazy pairs so that the evaluator can identify them in +order to print them. diff --git a/sicp/4_002e35 b/sicp/4_002e35 new file mode 100644 index 0000000..bc05c9e --- /dev/null +++ b/sicp/4_002e35 @@ -0,0 +1,48 @@ + +Exercise 4.35: Write a procedure +an-integer-between that returns an integer between two given bounds. +This can be used to implement a procedure that finds Pythagorean triples, i.e., +triples of integers + + ( + i + , + j + , + k + ) + + between the given bounds such that + + + i + ≤ + j + + and + + + i + 2 + + + + + j + 2 + + = + + k + 2 + + +, as follows: + + +(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))))) diff --git a/sicp/4_002e36 b/sicp/4_002e36 new file mode 100644 index 0000000..1a90cec --- /dev/null +++ b/sicp/4_002e36 @@ -0,0 +1,9 @@ + +Exercise 4.36: Exercise 3.69 discussed how +to generate the stream of all Pythagorean triples, with no upper bound +on the size of the integers to be searched. Explain why simply replacing +an-integer-between by an-integer-starting-from in the procedure +in Exercise 4.35 is not an adequate way to generate arbitrary Pythagorean +triples. Write a procedure that actually will accomplish this. (That is, +write a procedure for which repeatedly typing try-again would in +principle eventually generate all Pythagorean triples.) diff --git a/sicp/4_002e37 b/sicp/4_002e37 new file mode 100644 index 0000000..0a52595 --- /dev/null +++ b/sicp/4_002e37 @@ -0,0 +1,16 @@ + +Exercise 4.37: Ben Bitdiddle claims that the +following method for generating Pythagorean triples is more efficient than the +one in Exercise 4.35. Is he correct? (Hint: Consider the number of +possibilities that must be explored.) + + +(define (a-pythagorean-triple-between low high) + (let ((i (an-integer-between low high)) + (hsq (* high high))) + (let ((j (an-integer-between i high))) + (let ((ksq (+ (* i i) (* j j)))) + (require (>= hsq ksq)) + (let ((k (sqrt ksq))) + (require (integer? k)) + (list i j k)))))) diff --git a/sicp/4_002e38 b/sicp/4_002e38 new file mode 100644 index 0000000..b8fe6d2 --- /dev/null +++ b/sicp/4_002e38 @@ -0,0 +1,4 @@ + +Exercise 4.38: Modify the multiple-dwelling +procedure to omit the requirement that Smith and Fletcher do not live on +adjacent floors. How many solutions are there to this modified puzzle? diff --git a/sicp/4_002e39 b/sicp/4_002e39 new file mode 100644 index 0000000..d9e6acb --- /dev/null +++ b/sicp/4_002e39 @@ -0,0 +1,6 @@ + +Exercise 4.39: Does the order of the +restrictions in the multiple-dwelling procedure affect the answer? Does it +affect the time to find an answer? If you think it matters, demonstrate a +faster program obtained from the given one by reordering the restrictions. If +you think it does not matter, argue your case. diff --git a/sicp/4_002e4 b/sicp/4_002e4 new file mode 100644 index 0000000..99efe1a --- /dev/null +++ b/sicp/4_002e4 @@ -0,0 +1,22 @@ + +Exercise 4.4: Recall the definitions of the +special forms and and or from Chapter 1: + + + and: The expressions are evaluated from left to right. If any +expression evaluates to false, false is returned; any remaining expressions are +not evaluated. If all the expressions evaluate to true values, the value of +the last expression is returned. If there are no expressions then true is +returned. + + or: The expressions are evaluated from left to right. If any expression +evaluates to a true value, that value is returned; any remaining expressions +are not evaluated. If all expressions evaluate to false, or if there are no +expressions, then false is returned. + + + +Install and and or as new special forms for the evaluator by +defining appropriate syntax procedures and evaluation procedures +eval-and and eval-or. Alternatively, show how to implement +and and or as derived expressions. diff --git a/sicp/4_002e40 b/sicp/4_002e40 new file mode 100644 index 0000000..f66ee06 --- /dev/null +++ b/sicp/4_002e40 @@ -0,0 +1,12 @@ + +Exercise 4.40: In the multiple dwelling problem, +how many sets of assignments are there of people to floors, both before and +after the requirement that floor assignments be distinct? It is very +inefficient to generate all possible assignments of people to floors and then +leave it to backtracking to eliminate them. For example, most of the +restrictions depend on only one or two of the person-floor variables, and can +thus be imposed before floors have been selected for all the people. Write and +demonstrate a much more efficient nondeterministic procedure that solves this +problem based upon generating only those possibilities that are not already +ruled out by previous restrictions. (Hint: This will require a nest of +let expressions.) diff --git a/sicp/4_002e41 b/sicp/4_002e41 new file mode 100644 index 0000000..35044d6 --- /dev/null +++ b/sicp/4_002e41 @@ -0,0 +1,3 @@ + +Exercise 4.41: Write an ordinary Scheme program +to solve the multiple dwelling puzzle. diff --git a/sicp/4_002e42 b/sicp/4_002e42 new file mode 100644 index 0000000..925ddcc --- /dev/null +++ b/sicp/4_002e42 @@ -0,0 +1,24 @@ + +Exercise 4.42: Solve the following “Liars” +puzzle (from Phillips 1934): + +Five schoolgirls sat for an examination. Their parents—so they +thought—showed an undue degree of interest in the result. They therefore +agreed that, in writing home about the examination, each girl should make one +true statement and one untrue one. The following are the relevant passages +from their letters: + + + Betty: “Kitty was second in the examination. I was only third.” + + Ethel: “You’ll be glad to hear that I was on top. Joan was second.” + + Joan: “I was third, and poor old Ethel was bottom.” + + Kitty: “I came out second. Mary was only fourth.” + + Mary: “I was fourth. Top place was taken by Betty.” + + + +What in fact was the order in which the five girls were placed? diff --git a/sicp/4_002e43 b/sicp/4_002e43 new file mode 100644 index 0000000..1ba0cea --- /dev/null +++ b/sicp/4_002e43 @@ -0,0 +1,17 @@ + +Exercise 4.43: Use the amb evaluator to +solve the following puzzle:253 + + +Mary Ann Moore’s father has a yacht and so has each of his four friends: +Colonel Downing, Mr. Hall, Sir Barnacle Hood, and Dr. Parker. Each of the +five also has one daughter and each has named his yacht after a daughter of one +of the others. Sir Barnacle’s yacht is the Gabrielle, Mr. Moore owns the +Lorna; Mr. Hall the Rosalind. The Melissa, owned by Colonel Downing, is named +after Sir Barnacle’s daughter. Gabrielle’s father owns the yacht that is named +after Dr. Parker’s daughter. Who is Lorna’s father? + + +Try to write the program so that it runs efficiently (see Exercise 4.40). +Also determine how many solutions there are if we are not told that Mary Ann’s +last name is Moore. diff --git a/sicp/4_002e44 b/sicp/4_002e44 new file mode 100644 index 0000000..8e85d64 --- /dev/null +++ b/sicp/4_002e44 @@ -0,0 +1,4 @@ + +Exercise 4.44: Exercise 2.42 described the +“eight-queens puzzle” of placing queens on a chessboard so that no two attack +each other. Write a nondeterministic program to solve this puzzle. diff --git a/sicp/4_002e45 b/sicp/4_002e45 new file mode 100644 index 0000000..1ee4504 --- /dev/null +++ b/sicp/4_002e45 @@ -0,0 +1,5 @@ + +Exercise 4.45: With the grammar given above, the +following sentence can be parsed in five different ways: “The professor +lectures to the student in the class with the cat.” Give the five parses and +explain the differences in shades of meaning among them. diff --git a/sicp/4_002e46 b/sicp/4_002e46 new file mode 100644 index 0000000..1fbed0c --- /dev/null +++ b/sicp/4_002e46 @@ -0,0 +1,6 @@ + +Exercise 4.46: The evaluators in +4.1 and 4.2 do not determine what order operands are evaluated in. +We will see that the amb evaluator evaluates them from left to right. +Explain why our parsing program wouldn’t work if the operands were evaluated in +some other order. diff --git a/sicp/4_002e47 b/sicp/4_002e47 new file mode 100644 index 0000000..735880b --- /dev/null +++ b/sicp/4_002e47 @@ -0,0 +1,16 @@ + +Exercise 4.47: Louis Reasoner suggests that, +since a verb phrase is either a verb or a verb phrase followed by a +prepositional phrase, it would be much more straightforward to define the +procedure parse-verb-phrase as follows (and similarly for noun phrases): + + +(define (parse-verb-phrase) + (amb (parse-word verbs) + (list + 'verb-phrase + (parse-verb-phrase) + (parse-prepositional-phrase)))) + +Does this work? Does the program’s behavior change if we interchange the order +of expressions in the amb? diff --git a/sicp/4_002e48 b/sicp/4_002e48 new file mode 100644 index 0000000..93c29b4 --- /dev/null +++ b/sicp/4_002e48 @@ -0,0 +1,5 @@ + +Exercise 4.48: Extend the grammar given above to +handle more complex sentences. For example, you could extend noun phrases and +verb phrases to include adjectives and adverbs, or you could handle compound +sentences.257 diff --git a/sicp/4_002e49 b/sicp/4_002e49 new file mode 100644 index 0000000..59b83c2 --- /dev/null +++ b/sicp/4_002e49 @@ -0,0 +1,8 @@ + +Exercise 4.49: Alyssa P. Hacker is more +interested in generating interesting sentences than in parsing them. She +reasons that by simply changing the procedure parse-word so that it +ignores the “input sentence” and instead always succeeds and generates an +appropriate word, we can use the programs we had built for parsing to do +generation instead. Implement Alyssa’s idea, and show the first half-dozen or +so sentences generated.258 diff --git a/sicp/4_002e5 b/sicp/4_002e5 new file mode 100644 index 0000000..bb258a5 --- /dev/null +++ b/sicp/4_002e5 @@ -0,0 +1,14 @@ + +Exercise 4.5: Scheme allows an additional syntax +for cond clauses, (⟨test⟩ => ⟨recipient⟩). If +⟨test⟩ evaluates to a true value, then ⟨recipient⟩ is evaluated. +Its value must be a procedure of one argument; this procedure is then invoked +on the value of the ⟨test⟩, and the result is returned as the value of +the cond expression. For example + + +(cond ((assoc 'b '((a 1) (b 2))) => cadr) + (else false)) + +returns 2. Modify the handling of cond so that it supports this +extended syntax. diff --git a/sicp/4_002e50 b/sicp/4_002e50 new file mode 100644 index 0000000..4b38d78 --- /dev/null +++ b/sicp/4_002e50 @@ -0,0 +1,5 @@ + +Exercise 4.50: Implement a new special form +ramb that is like amb except that it searches alternatives in a +random order, rather than from left to right. Show how this can help with +Alyssa’s problem in Exercise 4.49. diff --git a/sicp/4_002e51 b/sicp/4_002e51 new file mode 100644 index 0000000..e5e52fe --- /dev/null +++ b/sicp/4_002e51 @@ -0,0 +1,27 @@ + +Exercise 4.51: Implement a new kind of +assignment called permanent-set! that is not undone upon failure. For +example, we can choose two distinct elements from a list and count the number +of trials required to make a successful choice as follows: + + +(define count 0) +(let ((x (an-element-of '(a b c))) + (y (an-element-of '(a b c)))) + (permanent-set! count (+ count 1)) + (require (not (eq? x y))) + (list x y count)) + +;;; Starting a new problem +;;; Amb-Eval value: +(a b 2) + +;;; Amb-Eval input: +try-again + +;;; Amb-Eval value: +(a c 3) + + +What values would have been displayed if we had used set! here +rather than permanent-set!? diff --git a/sicp/4_002e52 b/sicp/4_002e52 new file mode 100644 index 0000000..2440523 --- /dev/null +++ b/sicp/4_002e52 @@ -0,0 +1,31 @@ + +Exercise 4.52: Implement a new construct called +if-fail that permits the user to catch the failure of an expression. +If-fail takes two expressions. It evaluates the first expression as +usual and returns as usual if the evaluation succeeds. If the evaluation +fails, however, the value of the second expression is returned, as in the +following example: + + +;;; Amb-Eval input: +(if-fail + (let ((x (an-element-of '(1 3 5)))) + (require (even? x)) + x) + 'all-odd) + +;;; Starting a new problem +;;; Amb-Eval value: +all-odd + +;;; Amb-Eval input: +(if-fail + (let ((x (an-element-of '(1 3 5 8)))) + (require (even? x)) + x) + 'all-odd) + +;;; Starting a new problem +;;; Amb-Eval value: +8 + diff --git a/sicp/4_002e53 b/sicp/4_002e53 new file mode 100644 index 0000000..6407b98 --- /dev/null +++ b/sicp/4_002e53 @@ -0,0 +1,15 @@ + +Exercise 4.53: With permanent-set! as +described in Exercise 4.51 and if-fail as in Exercise 4.52, +what will be the result of evaluating + + +(let ((pairs '())) + (if-fail + (let ((p (prime-sum-pair + '(1 3 5 8) + '(20 35 110)))) + (permanent-set! pairs + (cons p pairs)) + (amb)) + pairs)) diff --git a/sicp/4_002e54 b/sicp/4_002e54 new file mode 100644 index 0000000..690b55f --- /dev/null +++ b/sicp/4_002e54 @@ -0,0 +1,33 @@ + +Exercise 4.54: If we had not realized that +require could be implemented as an ordinary procedure that uses +amb, to be defined by the user as part of a nondeterministic program, we +would have had to implement it as a special form. This would require syntax +procedures + + +(define (require? exp) + (tagged-list? exp 'require)) + +(define (require-predicate exp) + (cadr exp)) + +and a new clause in the dispatch in analyze + + +((require? exp) (analyze-require exp)) + +as well the procedure analyze-require that handles require +expressions. Complete the following definition of analyze-require. + + +(define (analyze-require exp) + (let ((pproc (analyze + (require-predicate exp)))) + (lambda (env succeed fail) + (pproc env + (lambda (pred-value fail2) + (if ⟨??⟩ + ⟨??⟩ + (succeed 'ok fail2))) + fail)))) diff --git a/sicp/4_002e55 b/sicp/4_002e55 new file mode 100644 index 0000000..4db1916 --- /dev/null +++ b/sicp/4_002e55 @@ -0,0 +1,12 @@ + +Exercise 4.55: Give simple queries that retrieve +the following information from the data base: + + + all people supervised by Ben Bitdiddle; + + the names and jobs of all people in the accounting division; + + the names and addresses of all people who live in Slumerville. + + diff --git a/sicp/4_002e56 b/sicp/4_002e56 new file mode 100644 index 0000000..a1b62cc --- /dev/null +++ b/sicp/4_002e56 @@ -0,0 +1,15 @@ + +Exercise 4.56: Formulate compound queries that +retrieve the following information: + + + the names of all people who are supervised by Ben Bitdiddle, together with +their addresses; + + all people whose salary is less than Ben Bitdiddle’s, together with their +salary and Ben Bitdiddle’s salary; + + all people who are supervised by someone who is not in the computer division, +together with the supervisor’s name and job. + + diff --git a/sicp/4_002e57 b/sicp/4_002e57 new file mode 100644 index 0000000..388940b --- /dev/null +++ b/sicp/4_002e57 @@ -0,0 +1,14 @@ + +Exercise 4.57: Define a rule that says that +person 1 can replace person 2 if either person 1 does the same job as person 2 +or someone who does person 1’s job can also do person 2’s job, and if person 1 +and person 2 are not the same person. Using your rule, give queries that find +the following: + + + all people who can replace Cy D. Fect; + + all people who can replace someone who is being paid more than they are, +together with the two salaries. + + diff --git a/sicp/4_002e58 b/sicp/4_002e58 new file mode 100644 index 0000000..8380cba --- /dev/null +++ b/sicp/4_002e58 @@ -0,0 +1,4 @@ + +Exercise 4.58: Define a rule that says that a +person is a “big shot” in a division if the person works in the division but +does not have a supervisor who works in the division. diff --git a/sicp/4_002e59 b/sicp/4_002e59 new file mode 100644 index 0000000..1f75947 --- /dev/null +++ b/sicp/4_002e59 @@ -0,0 +1,38 @@ + +Exercise 4.59: Ben Bitdiddle has missed one +meeting too many. Fearing that his habit of forgetting meetings could cost him +his job, Ben decides to do something about it. He adds all the weekly meetings +of the firm to the Microshaft data base by asserting the following: + + +(meeting accounting (Monday 9am)) +(meeting administration (Monday 10am)) +(meeting computer (Wednesday 3pm)) +(meeting administration (Friday 1pm)) + +Each of the above assertions is for a meeting of an entire division. Ben also +adds an entry for the company-wide meeting that spans all the divisions. All +of the company’s employees attend this meeting. + + +(meeting whole-company (Wednesday 4pm)) + + + On Friday morning, Ben wants to query the data base for all the meetings that +occur that day. What query should he use? + + Alyssa P. Hacker is unimpressed. She thinks it would be much more useful to be +able to ask for her meetings by specifying her name. So she designs a rule +that says that a person’s meetings include all whole-company meetings +plus all meetings of that person’s division. Fill in the body of Alyssa’s +rule. + + +(rule (meeting-time ?person ?day-and-time) + ⟨rule-body⟩) + + Alyssa arrives at work on Wednesday morning and wonders what meetings she has +to attend that day. Having defined the above rule, what query should she make +to find this out? + + diff --git a/sicp/4_002e6 b/sicp/4_002e6 new file mode 100644 index 0000000..feaeee5 --- /dev/null +++ b/sicp/4_002e6 @@ -0,0 +1,21 @@ + +Exercise 4.6: Let expressions are derived +expressions, because + + +(let ((⟨var₁⟩ ⟨exp₁⟩) … (⟨varₙ⟩ ⟨expₙ⟩)) + ⟨body⟩) + +is equivalent to + + +((lambda (⟨var₁⟩ … ⟨varₙ⟩) + ⟨body⟩) + ⟨exp₁⟩ + … + ⟨expₙ⟩) + +Implement a syntactic transformation let->combination that reduces +evaluating let expressions to evaluating combinations of the type shown +above, and add the appropriate clause to eval to handle let +expressions. diff --git a/sicp/4_002e60 b/sicp/4_002e60 new file mode 100644 index 0000000..398b9ff --- /dev/null +++ b/sicp/4_002e60 @@ -0,0 +1,22 @@ + +Exercise 4.60: By giving the query + + +(lives-near ?person (Hacker Alyssa P)) + +Alyssa P. Hacker is able to find people who live near her, with whom she can +ride to work. On the other hand, when she tries to find all pairs of people +who live near each other by querying + + +(lives-near ?person-1 ?person-2) + +she notices that each pair of people who live near each other is listed twice; +for example, + + +(lives-near (Hacker Alyssa P) (Fect Cy D)) +(lives-near (Fect Cy D) (Hacker Alyssa P)) + +Why does this happen? Is there a way to find a list of people who live near +each other, in which each pair appears only once? Explain. diff --git a/sicp/4_002e61 b/sicp/4_002e61 new file mode 100644 index 0000000..cd33e9b --- /dev/null +++ b/sicp/4_002e61 @@ -0,0 +1,14 @@ + +Exercise 4.61: The following rules implement a +next-to relation that finds adjacent elements of a list: + + +(rule (?x next-to ?y in (?x ?y . ?u))) +(rule (?x next-to ?y in (?v . ?z)) + (?x next-to ?y in ?z)) + +What will the response be to the following queries? + + +(?x next-to ?y in (1 (2 3) 4)) +(?x next-to 1 in (2 1 3 1)) diff --git a/sicp/4_002e62 b/sicp/4_002e62 new file mode 100644 index 0000000..d8fc169 --- /dev/null +++ b/sicp/4_002e62 @@ -0,0 +1,7 @@ + +Exercise 4.62: Define rules to implement the +last-pair operation of Exercise 2.17, which returns a list +containing the last element of a nonempty list. Check your rules on queries +such as (last-pair (3) ?x), (last-pair (1 2 3) ?x) and +(last-pair (2 ?x) (3)). Do your rules work correctly on queries such as +(last-pair ?x (3))? diff --git a/sicp/4_002e63 b/sicp/4_002e63 new file mode 100644 index 0000000..6aed832 --- /dev/null +++ b/sicp/4_002e63 @@ -0,0 +1,44 @@ + +Exercise 4.63: The following data base (see +Genesis 4) traces the genealogy of the descendants of Ada back to Adam, by way +of Cain: + + +(son Adam Cain) (son Cain Enoch) +(son Enoch Irad) (son Irad Mehujael) +(son Mehujael Methushael) +(son Methushael Lamech) +(wife Lamech Ada) (son Ada Jabal) +(son Ada Jubal) + +Formulate rules such as “If + S + is the son of + f +, and + f + is the son of + + G +, then + S + is the grandson of + G +” and “If + W + is the wife of + + M +, and + S + is the son of + W +, then + S + is the son of + M +” (which +was supposedly more true in biblical times than today) that will enable the +query system to find the grandson of Cain; the sons of Lamech; the grandsons of +Methushael. (See Exercise 4.69 for some rules to deduce more complicated +relationships.) diff --git a/sicp/4_002e64 b/sicp/4_002e64 new file mode 100644 index 0000000..7336080 --- /dev/null +++ b/sicp/4_002e64 @@ -0,0 +1,21 @@ + +Exercise 4.64: Louis Reasoner mistakenly deletes +the outranked-by rule (4.4.1) from the data base. When he +realizes this, he quickly reinstalls it. Unfortunately, he makes a slight +change in the rule, and types it in as + + +(rule (outranked-by ?staff-person ?boss) + (or (supervisor ?staff-person ?boss) + (and (outranked-by ?middle-manager + ?boss) + (supervisor ?staff-person + ?middle-manager)))) + +Just after Louis types this information into the system, DeWitt Aull comes by +to find out who outranks Ben Bitdiddle. He issues the query + + +(outranked-by (Bitdiddle Ben) ?who) + +After answering, the system goes into an infinite loop. Explain why. diff --git a/sicp/4_002e65 b/sicp/4_002e65 new file mode 100644 index 0000000..86be0a9 --- /dev/null +++ b/sicp/4_002e65 @@ -0,0 +1,19 @@ + +Exercise 4.65: Cy D. Fect, looking forward to +the day when he will rise in the organization, gives a query to find all the +wheels (using the wheel rule of 4.4.1): + + +(wheel ?who) + +To his surprise, the system responds + + +;;; Query results: +(wheel (Warbucks Oliver)) +(wheel (Bitdiddle Ben)) +(wheel (Warbucks Oliver)) +(wheel (Warbucks Oliver)) +(wheel (Warbucks Oliver)) + +Why is Oliver Warbucks listed four times? diff --git a/sicp/4_002e66 b/sicp/4_002e66 new file mode 100644 index 0000000..2eca419 --- /dev/null +++ b/sicp/4_002e66 @@ -0,0 +1,29 @@ + +Exercise 4.66: Ben has been generalizing the +query system to provide statistics about the company. For example, to find the +total salaries of all the computer programmers one will be able to say + + +(sum ?amount + (and (job ?x (computer programmer)) + (salary ?x ?amount))) + +In general, Ben’s new system allows expressions of the form + + +(accumulation-function ⟨variable⟩ + ⟨query pattern⟩) + +where accumulation-function can be things like sum, +average, or maximum. Ben reasons that it should be a cinch to +implement this. He will simply feed the query pattern to qeval. This +will produce a stream of frames. He will then pass this stream through a +mapping function that extracts the value of the designated variable from each +frame in the stream and feed the resulting stream of values to the accumulation +function. Just as Ben completes the implementation and is about to try it out, +Cy walks by, still puzzling over the wheel query result in +Exercise 4.65. When Cy shows Ben the system’s response, Ben groans, +“Oh, no, my simple accumulation scheme won’t work!” + +What has Ben just realized? Outline a method he can use to salvage the +situation. diff --git a/sicp/4_002e67 b/sicp/4_002e67 new file mode 100644 index 0000000..5a7fc89 --- /dev/null +++ b/sicp/4_002e67 @@ -0,0 +1,10 @@ + +Exercise 4.67: Devise a way to install a loop +detector in the query system so as to avoid the kinds of simple loops +illustrated in the text and in Exercise 4.64. The general idea is that +the system should maintain some sort of history of its current chain of +deductions and should not begin processing a query that it is already working +on. Describe what kind of information (patterns and frames) is included in +this history, and how the check should be made. (After you study the details +of the query-system implementation in 4.4.4, you may want to +modify the system to include your loop detector.) diff --git a/sicp/4_002e68 b/sicp/4_002e68 new file mode 100644 index 0000000..208670e --- /dev/null +++ b/sicp/4_002e68 @@ -0,0 +1,6 @@ + +Exercise 4.68: Define rules to implement the +reverse operation of Exercise 2.18, which returns a list +containing the same elements as a given list in reverse order. (Hint: Use +append-to-form.) Can your rules answer both (reverse (1 2 3) ?x) +and (reverse ?x (1 2 3))? diff --git a/sicp/4_002e69 b/sicp/4_002e69 new file mode 100644 index 0000000..3319b36 --- /dev/null +++ b/sicp/4_002e69 @@ -0,0 +1,12 @@ + +Exercise 4.69: Beginning with the data base and +the rules you formulated in Exercise 4.63, devise a rule for adding +“greats” to a grandson relationship. This should enable the system to deduce +that Irad is the great-grandson of Adam, or that Jabal and Jubal are the +great-great-great-great-great-grandsons of Adam. (Hint: Represent the fact +about Irad, for example, as ((great grandson) Adam Irad). Write rules +that determine if a list ends in the word grandson. Use this to express +a rule that allows one to derive the relationship ((great . ?rel) ?x +?y), where ?rel is a list ending in grandson.) Check your rules +on queries such as ((great grandson) ?g ?ggs) and (?relationship +Adam Irad). diff --git a/sicp/4_002e7 b/sicp/4_002e7 new file mode 100644 index 0000000..87c1679 --- /dev/null +++ b/sicp/4_002e7 @@ -0,0 +1,22 @@ + +Exercise 4.7: Let* is similar to +let, except that the bindings of the let* variables are performed +sequentially from left to right, and each binding is made in an environment in +which all of the preceding bindings are visible. For example + + +(let* ((x 3) + (y (+ x 2)) + (z (+ x y 5))) + (* x z)) + +returns 39. Explain how a let* expression can be rewritten as a set of +nested let expressions, and write a procedure let*->nested-lets +that performs this transformation. If we have already implemented let +(Exercise 4.6) and we want to extend the evaluator to handle let*, +is it sufficient to add a clause to eval whose action is + + +(eval (let*->nested-lets exp) env) + +or must we explicitly expand let* in terms of non-derived expressions? diff --git a/sicp/4_002e70 b/sicp/4_002e70 new file mode 100644 index 0000000..215065f --- /dev/null +++ b/sicp/4_002e70 @@ -0,0 +1,14 @@ + +Exercise 4.70: What is the purpose of the +let bindings in the procedures add-assertion! and +add-rule!? What would be wrong with the following implementation of +add-assertion!? Hint: Recall the definition of the infinite stream of +ones in 3.5.2: (define ones (cons-stream 1 ones)). + + +(define (add-assertion! assertion) + (store-assertion-in-index assertion) + (set! THE-ASSERTIONS + (cons-stream assertion + THE-ASSERTIONS)) + 'ok) diff --git a/sicp/4_002e71 b/sicp/4_002e71 new file mode 100644 index 0000000..66978b6 --- /dev/null +++ b/sicp/4_002e71 @@ -0,0 +1,27 @@ + +Exercise 4.71: Louis Reasoner wonders why the +simple-query and disjoin procedures (4.4.4.2) are +implemented using explicit delay operations, rather than being defined +as follows: + + +(define (simple-query + query-pattern frame-stream) + (stream-flatmap + (lambda (frame) + (stream-append + (find-assertions query-pattern frame) + (apply-rules query-pattern frame))) + frame-stream)) + +(define (disjoin disjuncts frame-stream) + (if (empty-disjunction? disjuncts) + the-empty-stream + (interleave + (qeval (first-disjunct disjuncts) + frame-stream) + (disjoin (rest-disjuncts disjuncts) + frame-stream)))) + +Can you give examples of queries where these simpler definitions would lead to +undesirable behavior? diff --git a/sicp/4_002e72 b/sicp/4_002e72 new file mode 100644 index 0000000..430a1eb --- /dev/null +++ b/sicp/4_002e72 @@ -0,0 +1,5 @@ + +Exercise 4.72: Why do disjoin and +stream-flatmap interleave the streams rather than simply append them? +Give examples that illustrate why interleaving works better. (Hint: Why did we +use interleave in 3.5.3?) diff --git a/sicp/4_002e73 b/sicp/4_002e73 new file mode 100644 index 0000000..e8c4953 --- /dev/null +++ b/sicp/4_002e73 @@ -0,0 +1,11 @@ + +Exercise 4.73: Why does flatten-stream +use delay explicitly? What would be wrong with defining it as follows: + + +(define (flatten-stream stream) + (if (stream-null? stream) + the-empty-stream + (interleave (stream-car stream) + (flatten-stream + (stream-cdr stream))))) diff --git a/sicp/4_002e74 b/sicp/4_002e74 new file mode 100644 index 0000000..b765a6d --- /dev/null +++ b/sicp/4_002e74 @@ -0,0 +1,22 @@ + +Exercise 4.74: Alyssa P. Hacker proposes to use +a simpler version of stream-flatmap in negate, lisp-value, +and find-assertions. She observes that the procedure that is mapped +over the frame stream in these cases always produces either the empty stream or +a singleton stream, so no interleaving is needed when combining these streams. + + + Fill in the missing expressions in Alyssa’s program. + + +(define (simple-stream-flatmap proc s) + (simple-flatten (stream-map proc s))) + +(define (simple-flatten stream) + (stream-map ⟨??⟩ + (stream-filter ⟨??⟩ + stream))) + + Does the query system’s behavior change if we change it in this way? + + diff --git a/sicp/4_002e75 b/sicp/4_002e75 new file mode 100644 index 0000000..6724aa5 --- /dev/null +++ b/sicp/4_002e75 @@ -0,0 +1,53 @@ + +Exercise 4.75: Implement for the query language +a new special form called unique. Unique should succeed if there +is precisely one item in the data base satisfying a specified query. For +example, + + +(unique (job ?x (computer wizard))) + +should print the one-item stream + + +(unique (job (Bitdiddle Ben) + (computer wizard))) + +since Ben is the only computer wizard, and + + +(unique (job ?x (computer programmer))) + +should print the empty stream, since there is more than one computer +programmer. Moreover, + + +(and (job ?x ?j) + (unique (job ?anyone ?j))) + +should list all the jobs that are filled by only one person, and the people who +fill them. + +There are two parts to implementing unique. The first is to write a +procedure that handles this special form, and the second is to make +qeval dispatch to that procedure. The second part is trivial, since +qeval does its dispatching in a data-directed way. If your procedure is +called uniquely-asserted, all you need to do is + + +(put 'unique 'qeval uniquely-asserted) + +and qeval will dispatch to this procedure for every query whose +type (car) is the symbol unique. + +The real problem is to write the procedure uniquely-asserted. This +should take as input the contents (cdr) of the unique +query, together with a stream of frames. For each frame in the stream, it +should use qeval to find the stream of all extensions to the frame that +satisfy the given query. Any stream that does not have exactly one item in it +should be eliminated. The remaining streams should be passed back to be +accumulated into one big stream that is the result of the unique query. +This is similar to the implementation of the not special form. + +Test your implementation by forming a query that lists all people who supervise +precisely one person. diff --git a/sicp/4_002e76 b/sicp/4_002e76 new file mode 100644 index 0000000..4287f88 --- /dev/null +++ b/sicp/4_002e76 @@ -0,0 +1,71 @@ + +Exercise 4.76: Our implementation of and +as a series combination of queries (Figure 4.5) is elegant, but it is +inefficient because in processing the second query of the and we must +scan the data base for each frame produced by the first query. If the data +base has + n + elements, and a typical query produces a number of output frames +proportional to + n + (say + + n + + + / + + + k + +), then scanning the data base for each +frame produced by the first query will require + + + n + 2 + + + / + + + k + + calls to the +pattern matcher. Another approach would be to process the two clauses of the +and separately, then look for all pairs of output frames that are +compatible. If each query produces + + n + + + / + + + k + + output frames, then this means +that we must perform + + + n + 2 + + + / + + + + k + 2 + + + compatibility checks—a factor of + k + +fewer than the number of matches required in our current method. + +Devise an implementation of and that uses this strategy. You must +implement a procedure that takes two frames as inputs, checks whether the +bindings in the frames are compatible, and, if so, produces a frame that merges +the two sets of bindings. This operation is similar to unification. diff --git a/sicp/4_002e77 b/sicp/4_002e77 new file mode 100644 index 0000000..8a07c9d --- /dev/null +++ b/sicp/4_002e77 @@ -0,0 +1,11 @@ + +Exercise 4.77: In 4.4.3 we saw +that not and lisp-value can cause the query language to give +“wrong” answers if these filtering operations are applied to frames in which +variables are unbound. Devise a way to fix this shortcoming. One idea is to +perform the filtering in a “delayed” manner by appending to the frame a +“promise” to filter that is fulfilled only when enough variables have been +bound to make the operation possible. We could wait to perform filtering until +all other operations have been performed. However, for efficiency’s sake, we +would like to perform filtering as soon as possible so as to cut down on the +number of intermediate frames generated. diff --git a/sicp/4_002e78 b/sicp/4_002e78 new file mode 100644 index 0000000..8fb0007 --- /dev/null +++ b/sicp/4_002e78 @@ -0,0 +1,10 @@ + +Exercise 4.78: Redesign the query language as a +nondeterministic program to be implemented using the evaluator of +4.3, rather than as a stream process. In this approach, each query will +produce a single answer (rather than the stream of all answers) and the user +can type try-again to see more answers. You should find that much of +the mechanism we built in this section is subsumed by nondeterministic search +and backtracking. You will probably also find, however, that your new query +language has subtle differences in behavior from the one implemented here. Can +you find examples that illustrate this difference? diff --git a/sicp/4_002e79 b/sicp/4_002e79 new file mode 100644 index 0000000..d032f2a --- /dev/null +++ b/sicp/4_002e79 @@ -0,0 +1,39 @@ + +Exercise 4.79: When we implemented the Lisp +evaluator in 4.1, we saw how to use local environments to avoid +name conflicts between the parameters of procedures. For example, in +evaluating + + +(define (square x) + (* x x)) + +(define (sum-of-squares x y) + (+ (square x) (square y))) + +(sum-of-squares 3 4) + +there is no confusion between the x in square and the x in +sum-of-squares, because we evaluate the body of each procedure in an +environment that is specially constructed to contain bindings for the local +variables. In the query system, we used a different strategy to avoid name +conflicts in applying rules. Each time we apply a rule we rename the variables +with new names that are guaranteed to be unique. The analogous strategy for +the Lisp evaluator would be to do away with local environments and simply +rename the variables in the body of a procedure each time we apply the +procedure. + +Implement for the query language a rule-application method that uses +environments rather than renaming. See if you can build on your environment +structure to create constructs in the query language for dealing with large +systems, such as the rule analog of block-structured procedures. Can you +relate any of this to the problem of making deductions in a context (e.g., “If +I supposed that + P + were true, then I would be able to deduce + A + and + + B +.”) as a method of problem solving? (This problem is open-ended. A good +answer is probably worth a Ph.D.) diff --git a/sicp/4_002e8 b/sicp/4_002e8 new file mode 100644 index 0000000..649fed7 --- /dev/null +++ b/sicp/4_002e8 @@ -0,0 +1,25 @@ + +Exercise 4.8: “Named let” is a variant +of let that has the form + + +(let ⟨var⟩ ⟨bindings⟩ ⟨body⟩) + +The ⟨bindings⟩ and ⟨body⟩ are just as in ordinary let, +except that ⟨var⟩ is bound within ⟨body⟩ to a procedure whose body +is ⟨body⟩ and whose parameters are the variables in the ⟨bindings⟩. +Thus, one can repeatedly execute the ⟨body⟩ by invoking the procedure +named ⟨var⟩. For example, the iterative Fibonacci procedure +(1.2.2) can be rewritten using named let as follows: + + +(define (fib n) + (let fib-iter ((a 1) (b 0) (count n)) + (if (= count 0) + b + (fib-iter (+ a b) + a + (- count 1))))) + +Modify let->combination of Exercise 4.6 to also support named +let. diff --git a/sicp/4_002e9 b/sicp/4_002e9 new file mode 100644 index 0000000..f8cfa99 --- /dev/null +++ b/sicp/4_002e9 @@ -0,0 +1,8 @@ + +Exercise 4.9: Many languages support a variety of +iteration constructs, such as do, for, while, and +until. In Scheme, iterative processes can be expressed in terms of +ordinary procedure calls, so special iteration constructs provide no essential +gain in computational power. On the other hand, such constructs are often +convenient. Design some iteration constructs, give examples of their use, and +show how to implement them as derived expressions. diff --git a/sicp/5_002e1 b/sicp/5_002e1 new file mode 100644 index 0000000..8cbfe1c --- /dev/null +++ b/sicp/5_002e1 @@ -0,0 +1,13 @@ + +Exercise 5.1: Design a register machine to +compute factorials using the iterative algorithm specified by the following +procedure. Draw data-path and controller diagrams for this machine. + + +(define (factorial n) + (define (iter product counter) + (if (> counter n) + product + (iter (* counter product) + (+ counter 1)))) + (iter 1 1)) diff --git a/sicp/5_002e10 b/sicp/5_002e10 new file mode 100644 index 0000000..4b6ec25 --- /dev/null +++ b/sicp/5_002e10 @@ -0,0 +1,5 @@ + +Exercise 5.10: Design a new syntax for +register-machine instructions and modify the simulator to use your new syntax. +Can you implement your new syntax without changing any part of the simulator +except the syntax procedures in this section? diff --git a/sicp/5_002e11 b/sicp/5_002e11 new file mode 100644 index 0000000..f973874 --- /dev/null +++ b/sicp/5_002e11 @@ -0,0 +1,31 @@ + +Exercise 5.11: When we introduced save +and restore in 5.1.4, we didn’t specify what would happen +if you tried to restore a register that was not the last one saved, as in the +sequence + + +(save y) +(save x) +(restore y) + +There are several reasonable possibilities for the meaning of restore: + + + (restore y) puts into y the last value saved on the stack, +regardless of what register that value came from. This is the way our +simulator behaves. Show how to take advantage of this behavior to eliminate +one instruction from the Fibonacci machine of 5.1.4 (Figure 5.12). + + (restore y) puts into y the last value saved on the stack, but +only if that value was saved from y; otherwise, it signals an error. +Modify the simulator to behave this way. You will have to change save +to put the register name on the stack along with the value. + + (restore y) puts into y the last value saved from y +regardless of what other registers were saved after y and not restored. +Modify the simulator to behave this way. You will have to associate a separate +stack with each register. You should make the initialize-stack +operation initialize all the register stacks. + + diff --git a/sicp/5_002e12 b/sicp/5_002e12 new file mode 100644 index 0000000..e3e2465 --- /dev/null +++ b/sicp/5_002e12 @@ -0,0 +1,26 @@ + +Exercise 5.12: The simulator can be used to help +determine the data paths required for implementing a machine with a given +controller. Extend the assembler to store the following information in the +machine model: + + + a list of all instructions, with duplicates removed, sorted by instruction type +(assign, goto, and so on); + + a list (without duplicates) of the registers used to hold entry points (these +are the registers referenced by goto instructions); + + a list (without duplicates) of the registers that are saved +or restored; + + for each register, a list (without duplicates) of the sources from which it is +assigned (for example, the sources for register val in the factorial +machine of Figure 5.11 are (const 1) and ((op *) (reg n) +(reg val))). + + + +Extend the message-passing interface to the machine to provide access to this +new information. To test your analyzer, define the Fibonacci machine from +Figure 5.12 and examine the lists you constructed. diff --git a/sicp/5_002e13 b/sicp/5_002e13 new file mode 100644 index 0000000..19fd3c6 --- /dev/null +++ b/sicp/5_002e13 @@ -0,0 +1,7 @@ + +Exercise 5.13: Modify the simulator so that it +uses the controller sequence to determine what registers the machine has rather +than requiring a list of registers as an argument to make-machine. +Instead of pre-allocating the registers in make-machine, you can +allocate them one at a time when they are first seen during assembly of the +instructions. diff --git a/sicp/5_002e14 b/sicp/5_002e14 new file mode 100644 index 0000000..7764496 --- /dev/null +++ b/sicp/5_002e14 @@ -0,0 +1,38 @@ + +Exercise 5.14: Measure the number of pushes and +the maximum stack depth required to compute + + n + ! + + for various small values of + + n + using the factorial machine shown in Figure 5.11. From your data +determine formulas in terms of + n + for the total number of push operations +and the maximum stack depth used in computing + + n + ! + + for any + + n + > + 1 + +. Note +that each of these is a linear function of + n + and is thus determined by two +constants. In order to get the statistics printed, you will have to augment +the factorial machine with instructions to initialize the stack and print the +statistics. You may want to also modify the machine so that it repeatedly +reads a value for + n +, computes the factorial, and prints the result (as we +did for the GCD machine in Figure 5.4), so that you will not +have to repeatedly invoke get-register-contents, +set-register-contents!, and start. diff --git a/sicp/5_002e15 b/sicp/5_002e15 new file mode 100644 index 0000000..2a380be --- /dev/null +++ b/sicp/5_002e15 @@ -0,0 +1,7 @@ + +Exercise 5.15: Add +instruction counting +to the register machine simulation. That is, have the machine model +keep track of the number of instructions executed. Extend the machine model’s +interface to accept a new message that prints the value of the instruction +count and resets the count to zero. diff --git a/sicp/5_002e16 b/sicp/5_002e16 new file mode 100644 index 0000000..bec223c --- /dev/null +++ b/sicp/5_002e16 @@ -0,0 +1,7 @@ + +Exercise 5.16: Augment the simulator to provide +for +instruction tracing. That is, before each instruction is +executed, the simulator should print the text of the instruction. Make the +machine model accept trace-on and trace-off messages to turn +tracing on and off. diff --git a/sicp/5_002e17 b/sicp/5_002e17 new file mode 100644 index 0000000..851396e --- /dev/null +++ b/sicp/5_002e17 @@ -0,0 +1,7 @@ + +Exercise 5.17: Extend the instruction tracing of +Exercise 5.16 so that before printing an instruction, the simulator +prints any labels that immediately precede that instruction in the controller +sequence. Be careful to do this in a way that does not interfere with +instruction counting (Exercise 5.15). You will have to make the +simulator retain the necessary label information. diff --git a/sicp/5_002e18 b/sicp/5_002e18 new file mode 100644 index 0000000..b205c93 --- /dev/null +++ b/sicp/5_002e18 @@ -0,0 +1,8 @@ + +Exercise 5.18: Modify the make-register +procedure of 5.2.1 so that registers can be traced. Registers +should accept messages that turn tracing on and off. When a register is +traced, assigning a value to the register should print the name of the +register, the old contents of the register, and the new contents being +assigned. Extend the interface to the machine model to permit you to turn +tracing on and off for designated machine registers. diff --git a/sicp/5_002e19 b/sicp/5_002e19 new file mode 100644 index 0000000..5985aad --- /dev/null +++ b/sicp/5_002e19 @@ -0,0 +1,44 @@ + +Exercise 5.19: Alyssa P. Hacker wants a + +breakpoint feature in the simulator to help her debug her machine +designs. You have been hired to install this feature for her. She wants to be +able to specify a place in the controller sequence where the simulator will +stop and allow her to examine the state of the machine. You are to implement a +procedure + + +(set-breakpoint ⟨machine⟩ ⟨label⟩ ⟨n⟩) + +that sets a breakpoint just before the + + n + + th + + + instruction after the given +label. For example, + + +(set-breakpoint gcd-machine 'test-b 4) + +installs a breakpoint in gcd-machine just before the assignment to +register a. When the simulator reaches the breakpoint it should print +the label and the offset of the breakpoint and stop executing instructions. +Alyssa can then use get-register-contents and +set-register-contents! to manipulate the state of the simulated machine. +She should then be able to continue execution by saying + + +(proceed-machine ⟨machine⟩) + +She should also be able to remove a specific breakpoint by means of + + +(cancel-breakpoint ⟨machine⟩ ⟨label⟩ ⟨n⟩) + +or to remove all breakpoints by means of + + +(cancel-all-breakpoints ⟨machine⟩) diff --git a/sicp/5_002e2 b/sicp/5_002e2 new file mode 100644 index 0000000..f4974cc --- /dev/null +++ b/sicp/5_002e2 @@ -0,0 +1,3 @@ + +Exercise 5.2: Use the register-machine language +to describe the iterative factorial machine of Exercise 5.1. diff --git a/sicp/5_002e20 b/sicp/5_002e20 new file mode 100644 index 0000000..a197154 --- /dev/null +++ b/sicp/5_002e20 @@ -0,0 +1,11 @@ + +Exercise 5.20: Draw the box-and-pointer +representation and the memory-vector representation (as in Figure 5.14) +of the list structure produced by + + +(define x (cons 1 2)) +(define y (list x x)) + +with the free pointer initially p1. What is the final value of +free? What pointers represent the values of x and y? diff --git a/sicp/5_002e21 b/sicp/5_002e21 new file mode 100644 index 0000000..3f5ca65 --- /dev/null +++ b/sicp/5_002e21 @@ -0,0 +1,30 @@ + +Exercise 5.21: Implement register machines for +the following procedures. Assume that the list-structure memory operations are +available as machine primitives. + + + Recursive count-leaves: + + +(define (count-leaves tree) + (cond ((null? tree) 0) + ((not (pair? tree)) 1) + (else + (+ (count-leaves (car tree)) + (count-leaves (cdr tree)))))) + + Recursive count-leaves with explicit counter: + + +(define (count-leaves tree) + (define (count-iter tree n) + (cond ((null? tree) n) + ((not (pair? tree)) (+ n 1)) + (else + (count-iter + (cdr tree) + (count-iter (car tree) + n))))) + (count-iter tree 0)) + diff --git a/sicp/5_002e22 b/sicp/5_002e22 new file mode 100644 index 0000000..1073364 --- /dev/null +++ b/sicp/5_002e22 @@ -0,0 +1,6 @@ + +Exercise 5.22: Exercise 3.12 of +3.3.1 presented an append procedure that appends two lists to form +a new list and an append! procedure that splices two lists together. +Design a register machine to implement each of these procedures. Assume that +the list-structure memory operations are available as primitive operations. diff --git a/sicp/5_002e23 b/sicp/5_002e23 new file mode 100644 index 0000000..6ecb203 --- /dev/null +++ b/sicp/5_002e23 @@ -0,0 +1,5 @@ + +Exercise 5.23: Extend the evaluator to handle +derived expressions such as cond, let, and so on +(4.1.2). You may “cheat” and assume that the syntax transformers such +as cond->if are available as machine operations.313 diff --git a/sicp/5_002e24 b/sicp/5_002e24 new file mode 100644 index 0000000..2ce7a68 --- /dev/null +++ b/sicp/5_002e24 @@ -0,0 +1,6 @@ + +Exercise 5.24: Implement cond as a new +basic special form without reducing it to if. You will have to +construct a loop that tests the predicates of successive cond clauses +until you find one that is true, and then use ev-sequence to evaluate +the actions of the clause. diff --git a/sicp/5_002e25 b/sicp/5_002e25 new file mode 100644 index 0000000..b9cedfb --- /dev/null +++ b/sicp/5_002e25 @@ -0,0 +1,3 @@ + +Exercise 5.25: Modify the evaluator so that it +uses normal-order evaluation, based on the lazy evaluator of 4.2. diff --git a/sicp/5_002e26 b/sicp/5_002e26 new file mode 100644 index 0000000..3d2febb --- /dev/null +++ b/sicp/5_002e26 @@ -0,0 +1,58 @@ + +Exercise 5.26: Use the monitored stack to +explore the tail-recursive property of the evaluator (5.4.2). +Start the evaluator and define the iterative factorial procedure from +1.2.1: + + +(define (factorial n) + (define (iter product counter) + (if (> counter n) + product + (iter (* counter product) + (+ counter 1)))) + (iter 1 1)) + +Run the procedure with some small values of + n +. Record the maximum stack +depth and the number of pushes required to compute + + n + ! + + for each of these +values. + + + You will find that the maximum depth required to evaluate + + n + ! + + is independent +of + n +. What is that depth? + + Determine from your data a formula in terms of + n + for the total number of +push operations used in evaluating + + n + ! + + for any + + n + ≥ + 1 + +. Note that the +number of operations used is a linear function of + n + and is thus determined +by two constants. + + diff --git a/sicp/5_002e27 b/sicp/5_002e27 new file mode 100644 index 0000000..40450b5 --- /dev/null +++ b/sicp/5_002e27 @@ -0,0 +1,84 @@ + +Exercise 5.27: For comparison with Exercise 5.26, +explore the behavior of the following procedure for computing factorials +recursively: + + +(define (factorial n) + (if (= n 1) + 1 + (* (factorial (- n 1)) n))) + +By running this procedure with the monitored stack, determine, as a function of + + n +, the maximum depth of the stack and the total number of pushes used in +evaluating + + n + ! + + for + + n + ≥ + 1 + +. (Again, these functions will be linear.) +Summarize your experiments by filling in the following table with the +appropriate expressions in terms of + n +: + + + + + + Maximum + + + Number of + + + + + + depth + + + pushes + + + + + Recursive + + + + + + + factorial + + + + + + + Iterative + + + + + + + factorial + + + + + + +The maximum depth is a measure of the amount of space used by the evaluator in +carrying out the computation, and the number of pushes correlates well with the +time required. diff --git a/sicp/5_002e28 b/sicp/5_002e28 new file mode 100644 index 0000000..7b46f22 --- /dev/null +++ b/sicp/5_002e28 @@ -0,0 +1,7 @@ + +Exercise 5.28: Modify the definition of the +evaluator by changing eval-sequence as described in 5.4.2 +so that the evaluator is no longer tail-recursive. Rerun your experiments from +Exercise 5.26 and Exercise 5.27 to demonstrate that both versions +of the factorial procedure now require space that grows linearly with +their input. diff --git a/sicp/5_002e29 b/sicp/5_002e29 new file mode 100644 index 0000000..a4dce05 --- /dev/null +++ b/sicp/5_002e29 @@ -0,0 +1,127 @@ + +Exercise 5.29: Monitor the stack operations in +the tree-recursive Fibonacci computation: + + +(define (fib n) + (if (< n 2) + n + (+ (fib (- n 1)) (fib (- n 2))))) + + + Give a formula in terms of + n + for the maximum depth of the stack required to +compute + + Fib + ( + n + ) + + for + + n + ≥ + 2 + +. Hint: In 1.2.2 we +argued that the space used by this process grows linearly with + n +. + + Give a formula for the total number of pushes used to compute + + Fib + ( + n + ) + + +for + + n + ≥ + 2 + +. You should find that the number of pushes (which correlates +well with the time used) grows exponentially with + n +. Hint: Let + + + S + ( + n + ) + + be the number of pushes used in computing + + Fib + ( + n + ) + +. You +should be able to argue that there is a formula that expresses + + S + ( + n + ) + + in +terms of + + S + ( + n + − + 1 + ) + +, + + S + ( + n + − + 2 + ) + +, and some fixed “overhead” +constant + k + that is independent of + n +. Give the formula, and say what + + k + is. Then show that + + S + ( + n + ) + + can be expressed as + + + a + ⋅ + Fib + ( + n + + + 1 + ) + + + b + + and give the values of + a + and + b +. + + diff --git a/sicp/5_002e3 b/sicp/5_002e3 new file mode 100644 index 0000000..96b8035 --- /dev/null +++ b/sicp/5_002e3 @@ -0,0 +1,21 @@ + +Exercise 5.3: Design a machine to compute square +roots using Newton’s method, as described in 1.1.7: + + +(define (sqrt x) + (define (good-enough? guess) + (< (abs (- (square guess) x)) 0.001)) + (define (improve guess) + (average guess (/ x guess))) + (define (sqrt-iter guess) + (if (good-enough? guess) + guess + (sqrt-iter (improve guess)))) + (sqrt-iter 1.0)) + +Begin by assuming that good-enough? and improve operations are +available as primitives. Then show how to expand these in terms of arithmetic +operations. Describe each version of the sqrt machine design by drawing +a data-path diagram and writing a controller definition in the register-machine +language. diff --git a/sicp/5_002e30 b/sicp/5_002e30 new file mode 100644 index 0000000..85953f3 --- /dev/null +++ b/sicp/5_002e30 @@ -0,0 +1,34 @@ + +Exercise 5.30: Our evaluator currently catches +and signals only two kinds of errors—unknown expression types and unknown +procedure types. Other errors will take us out of the evaluator +read-eval-print loop. When we run the evaluator using the register-machine +simulator, these errors are caught by the underlying Scheme system. This is +analogous to the computer crashing when a user program makes an +error.317 It is a +large project to make a real error system work, but it is well worth the effort +to understand what is involved here. + + + Errors that occur in the evaluation process, such as an attempt to access an +unbound variable, could be caught by changing the lookup operation to make it +return a distinguished condition code, which cannot be a possible value of any +user variable. The evaluator can test for this condition code and then do what +is necessary to go to signal-error. Find all of the places in the +evaluator where such a change is necessary and fix them. This is lots of work. + + Much worse is the problem of handling errors that are signaled by applying +primitive procedures, such as an attempt to divide by zero or an attempt to +extract the car of a symbol. In a professionally written high-quality +system, each primitive application is checked for safety as part of the +primitive. For example, every call to car could first check that the +argument is a pair. If the argument is not a pair, the application would +return a distinguished condition code to the evaluator, which would then report +the failure. We could arrange for this in our register-machine simulator by +making each primitive procedure check for applicability and returning an +appropriate distinguished condition code on failure. Then the +primitive-apply code in the evaluator can check for the condition code +and go to signal-error if necessary. Build this structure and make it +work. This is a major project. + + diff --git a/sicp/5_002e31 b/sicp/5_002e31 new file mode 100644 index 0000000..2c3d1cb --- /dev/null +++ b/sicp/5_002e31 @@ -0,0 +1,16 @@ + +Exercise 5.31: In evaluating a procedure +application, the explicit-control evaluator always saves and restores the +env register around the evaluation of the operator, saves and restores +env around the evaluation of each operand (except the final one), saves +and restores argl around the evaluation of each operand, and saves and +restores proc around the evaluation of the operand sequence. For each +of the following combinations, say which of these save and +restore operations are superfluous and thus could be eliminated by the +compiler’s preserving mechanism: + + +(f 'x 'y) +((f) 'x 'y) +(f (g 'x) y) +(f (g 'x) 'y) diff --git a/sicp/5_002e32 b/sicp/5_002e32 new file mode 100644 index 0000000..12cf0e1 --- /dev/null +++ b/sicp/5_002e32 @@ -0,0 +1,19 @@ + +Exercise 5.32: Using the preserving +mechanism, the compiler will avoid saving and restoring env around the +evaluation of the operator of a combination in the case where the operator is a +symbol. We could also build such optimizations into the evaluator. Indeed, +the explicit-control evaluator of 5.4 already performs a similar +optimization, by treating combinations with no operands as a special case. + + + Extend the explicit-control evaluator to recognize as a separate class of +expressions combinations whose operator is a symbol, and to take advantage of +this fact in evaluating such expressions. + + Alyssa P. Hacker suggests that by extending the evaluator to recognize more and +more special cases we could incorporate all the compiler’s optimizations, and +that this would eliminate the advantage of compilation altogether. What do you +think of this idea? + + diff --git a/sicp/5_002e33 b/sicp/5_002e33 new file mode 100644 index 0000000..7288dcc --- /dev/null +++ b/sicp/5_002e33 @@ -0,0 +1,13 @@ + +Exercise 5.33: Consider the following definition +of a factorial procedure, which is slightly different from the one given above: + + +(define (factorial-alt n) + (if (= n 1) + 1 + (* n (factorial-alt (- n 1))))) + +Compile this procedure and compare the resulting code with that produced for +factorial. Explain any differences you find. Does either program +execute more efficiently than the other? diff --git a/sicp/5_002e34 b/sicp/5_002e34 new file mode 100644 index 0000000..ad29486 --- /dev/null +++ b/sicp/5_002e34 @@ -0,0 +1,16 @@ + +Exercise 5.34: Compile the iterative factorial +procedure + + +(define (factorial n) + (define (iter product counter) + (if (> counter n) + product + (iter (* counter product) + (+ counter 1)))) + (iter 1 1)) + +Annotate the resulting code, showing the essential difference between the code +for iterative and recursive versions of factorial that makes one process +build up stack space and the other run in constant stack space. diff --git a/sicp/5_002e35 b/sicp/5_002e35 new file mode 100644 index 0000000..4c7de17 --- /dev/null +++ b/sicp/5_002e35 @@ -0,0 +1,3 @@ + +Exercise 5.35: What expression was compiled to +produce the code shown in Figure 5.18? diff --git a/sicp/5_002e36 b/sicp/5_002e36 new file mode 100644 index 0000000..61ffdb0 --- /dev/null +++ b/sicp/5_002e36 @@ -0,0 +1,9 @@ + +Exercise 5.36: What order of evaluation does our +compiler produce for operands of a combination? Is it left-to-right, +right-to-left, or some other order? Where in the compiler is this order +determined? Modify the compiler so that it produces some other order of +evaluation. (See the discussion of order of evaluation for the +explicit-control evaluator in 5.4.1.) How does changing the +order of operand evaluation affect the efficiency of the code that constructs +the argument list? diff --git a/sicp/5_002e37 b/sicp/5_002e37 new file mode 100644 index 0000000..9489bf7 --- /dev/null +++ b/sicp/5_002e37 @@ -0,0 +1,8 @@ + +Exercise 5.37: One way to understand the +compiler’s preserving mechanism for optimizing stack usage is to see +what extra operations would be generated if we did not use this idea. Modify +preserving so that it always generates the save and +restore operations. Compile some simple expressions and identify the +unnecessary stack operations that are generated. Compare the code to that +generated with the preserving mechanism intact. diff --git a/sicp/5_002e38 b/sicp/5_002e38 new file mode 100644 index 0000000..61eab92 --- /dev/null +++ b/sicp/5_002e38 @@ -0,0 +1,67 @@ + +Exercise 5.38: Our compiler is clever about +avoiding unnecessary stack operations, but it is not clever at all when it +comes to compiling calls to the primitive procedures of the language in terms +of the primitive operations supplied by the machine. For example, consider how +much code is compiled to compute (+ a 1): The code sets up an argument +list in argl, puts the primitive addition procedure (which it finds by +looking up the symbol + in the environment) into proc, and tests +whether the procedure is primitive or compound. The compiler always generates +code to perform the test, as well as code for primitive and compound branches +(only one of which will be executed). We have not shown the part of the +controller that implements primitives, but we presume that these instructions +make use of primitive arithmetic operations in the machine’s data paths. +Consider how much less code would be generated if the compiler could + +open-code primitives—that is, if it could generate code to directly +use these primitive machine operations. The expression (+ a 1) might be +compiled into something as simple as328 + + +(assign val (op lookup-variable-value) + (const a) + (reg env)) +(assign val (op +) + (reg val) + (const 1)) + +In this exercise we will extend our compiler to support open coding of selected +primitives. Special-purpose code will be generated for calls to these +primitive procedures instead of the general procedure-application code. In +order to support this, we will augment our machine with special argument +registers arg1 and arg2. The primitive arithmetic operations of +the machine will take their inputs from arg1 and arg2. The +results may be put into val, arg1, or arg2. + +The compiler must be able to recognize the application of an open-coded +primitive in the source program. We will augment the dispatch in the +compile procedure to recognize the names of these primitives in addition +to the reserved words (the special forms) it currently +recognizes.329 For each special +form our compiler has a code generator. In this exercise we will construct a +family of code generators for the open-coded primitives. + + + The open-coded primitives, unlike the special forms, all need their operands +evaluated. Write a code generator spread-arguments for use by all the +open-coding code generators. Spread-arguments should take an operand +list and compile the given operands targeted to successive argument registers. +Note that an operand may contain a call to an open-coded primitive, so argument +registers will have to be preserved during operand evaluation. + + For each of the primitive procedures =, *, -, and ++, write a code generator that takes a combination with that operator, +together with a target and a linkage descriptor, and produces code to spread +the arguments into the registers and then perform the operation targeted to the +given target with the given linkage. You need only handle expressions with two +operands. Make compile dispatch to these code generators. + + Try your new compiler on the factorial example. Compare the resulting +code with the result produced without open coding. + + Extend your code generators for + and * so that they can handle +expressions with arbitrary numbers of operands. An expression with more than +two operands will have to be compiled into a sequence of operations, each with +only two inputs. + + diff --git a/sicp/5_002e39 b/sicp/5_002e39 new file mode 100644 index 0000000..8edeeed --- /dev/null +++ b/sicp/5_002e39 @@ -0,0 +1,9 @@ + +Exercise 5.39: Write a procedure +lexical-address-lookup that implements the new lookup operation. It +should take two arguments—a lexical address and a run-time environment—and +return the value of the variable stored at the specified lexical address. +Lexical-address-lookup should signal an error if the value of the +variable is the symbol *unassigned*.331 Also write a procedure +lexical-address-set! that implements the operation that changes the +value of the variable at a specified lexical address. diff --git a/sicp/5_002e4 b/sicp/5_002e4 new file mode 100644 index 0000000..c98dca3 --- /dev/null +++ b/sicp/5_002e4 @@ -0,0 +1,26 @@ + +Exercise 5.4: Specify register machines that +implement each of the following procedures. For each machine, write a +controller instruction sequence and draw a diagram showing the data paths. + + + Recursive exponentiation: + + +(define (expt b n) + (if (= n 0) + 1 + (* b (expt b (- n 1))))) + + Iterative exponentiation: + + +(define (expt b n) + (define (expt-iter counter product) + (if (= counter 0) + product + (expt-iter (- counter 1) + (* b product)))) + (expt-iter n 1)) + + diff --git a/sicp/5_002e40 b/sicp/5_002e40 new file mode 100644 index 0000000..d569916 --- /dev/null +++ b/sicp/5_002e40 @@ -0,0 +1,5 @@ + +Exercise 5.40: Modify the compiler to maintain +the compile-time environment as described above. That is, add a +compile-time-environment argument to compile and the various code +generators, and extend it in compile-lambda-body. diff --git a/sicp/5_002e41 b/sicp/5_002e41 new file mode 100644 index 0000000..3e3a750 --- /dev/null +++ b/sicp/5_002e41 @@ -0,0 +1,21 @@ + +Exercise 5.41: Write a procedure +find-variable that takes as arguments a variable and a compile-time +environment and returns the lexical address of the variable with respect to +that environment. For example, in the program fragment that is shown above, +the compile-time environment during the compilation of expression ⟨e1⟩ is +((y z) (a b c d e) (x y)). Find-variable should produce + + +(find-variable + 'c '((y z) (a b c d e) (x y))) +(1 2) + +(find-variable + 'x '((y z) (a b c d e) (x y))) +(2 0) + +(find-variable + 'w '((y z) (a b c d e) (x y))) +not-found + diff --git a/sicp/5_002e42 b/sicp/5_002e42 new file mode 100644 index 0000000..097f3bd --- /dev/null +++ b/sicp/5_002e42 @@ -0,0 +1,15 @@ + +Exercise 5.42: Using find-variable from +Exercise 5.41, rewrite compile-variable and +compile-assignment to output lexical-address instructions. In cases +where find-variable returns not-found (that is, where the +variable is not in the compile-time environment), you should have the code +generators use the evaluator operations, as before, to search for the binding. +(The only place a variable that is not found at compile time can be is in the +global environment, which is part of the run-time environment but is not part +of the compile-time environment.332 Thus, if you wish, you may have the evaluator operations look +directly in the global environment, which can be obtained with the operation +(op get-global-environment), instead of having them search the whole +run-time environment found in env.) Test the modified compiler on a few +simple cases, such as the nested lambda combination at the beginning of +this section. diff --git a/sicp/5_002e43 b/sicp/5_002e43 new file mode 100644 index 0000000..3deb399 --- /dev/null +++ b/sicp/5_002e43 @@ -0,0 +1,10 @@ + +Exercise 5.43: We argued in 4.1.6 +that internal definitions for block structure should not be considered “real” +defines. Rather, a procedure body should be interpreted as if the +internal variables being defined were installed as ordinary lambda +variables initialized to their correct values using set!. +4.1.6 and Exercise 4.16 showed how to modify the metacircular +interpreter to accomplish this by scanning out internal definitions. Modify +the compiler to perform the same transformation before it compiles a procedure +body. diff --git a/sicp/5_002e44 b/sicp/5_002e44 new file mode 100644 index 0000000..6b23a6a --- /dev/null +++ b/sicp/5_002e44 @@ -0,0 +1,22 @@ + +Exercise 5.44: In this section we have focused +on the use of the compile-time environment to produce lexical addresses. But +there are other uses for compile-time environments. For instance, in +Exercise 5.38 we increased the efficiency of compiled code by open-coding +primitive procedures. Our implementation treated the names of open-coded +procedures as reserved words. If a program were to rebind such a name, the +mechanism described in Exercise 5.38 would still open-code it as a +primitive, ignoring the new binding. For example, consider the procedure + + +(lambda (+ * a b x y) + (+ (* a x) (* b y))) + +which computes a linear combination of x and y. We might call it +with arguments +matrix, *matrix, and four matrices, but the +open-coding compiler would still open-code the + and the * in +(+ (* a x) (* b y)) as primitive + and *. Modify the +open-coding compiler to consult the compile-time environment in order to +compile the correct code for expressions involving the names of primitive +procedures. (The code will work correctly as long as the program does not +define or set! these names.) diff --git a/sicp/5_002e45 b/sicp/5_002e45 new file mode 100644 index 0000000..6ae479a --- /dev/null +++ b/sicp/5_002e45 @@ -0,0 +1,50 @@ + +Exercise 5.45: By comparing the stack operations +used by compiled code to the stack operations used by the evaluator for the +same computation, we can determine the extent to which the compiler optimizes +use of the stack, both in speed (reducing the total number of stack operations) +and in space (reducing the maximum stack depth). Comparing this optimized +stack use to the performance of a special-purpose machine for the same +computation gives some indication of the quality of the compiler. + + + Exercise 5.27 asked you to determine, as a function of + n +, the number +of pushes and the maximum stack depth needed by the evaluator to compute + + n + ! + + +using the recursive factorial procedure given above. Exercise 5.14 asked +you to do the same measurements for the special-purpose factorial machine shown +in Figure 5.11. Now perform the same analysis using the compiled +factorial procedure. + +Take the ratio of the number of pushes in the compiled version to the number of +pushes in the interpreted version, and do the same for the maximum stack depth. +Since the number of operations and the stack depth used to compute + + n + ! + + are +linear in + n +, these ratios should approach constants as + n + becomes large. +What are these constants? Similarly, find the ratios of the stack usage in the +special-purpose machine to the usage in the interpreted version. + +Compare the ratios for special-purpose versus interpreted code to the ratios +for compiled versus interpreted code. You should find that the special-purpose +machine does much better than the compiled code, since the hand-tailored +controller code should be much better than what is produced by our rudimentary +general-purpose compiler. + + Can you suggest improvements to the compiler that would help it generate code +that would come closer in performance to the hand-tailored version? + + diff --git a/sicp/5_002e46 b/sicp/5_002e46 new file mode 100644 index 0000000..a1490b5 --- /dev/null +++ b/sicp/5_002e46 @@ -0,0 +1,23 @@ + +Exercise 5.46: Carry out an analysis like the +one in Exercise 5.45 to determine the effectiveness of compiling the +tree-recursive Fibonacci procedure + + +(define (fib n) + (if (< n 2) + n + (+ (fib (- n 1)) (fib (- n 2))))) + +compared to the effectiveness of using the special-purpose Fibonacci machine of +Figure 5.12. (For measurement of the interpreted performance, see +Exercise 5.29.) For Fibonacci, the time resource used is not linear in + + + n + ; + + hence the ratios of stack operations will not approach a limiting value +that is independent of + n +. diff --git a/sicp/5_002e47 b/sicp/5_002e47 new file mode 100644 index 0000000..4d685e7 --- /dev/null +++ b/sicp/5_002e47 @@ -0,0 +1,27 @@ + +Exercise 5.47: This section described how to +modify the explicit-control evaluator so that interpreted code can call +compiled procedures. Show how to modify the compiler so that compiled +procedures can call not only primitive procedures and compiled procedures, but +interpreted procedures as well. This requires modifying +compile-procedure-call to handle the case of compound (interpreted) +procedures. Be sure to handle all the same target and linkage +combinations as in compile-proc-appl. To do the actual procedure +application, the code needs to jump to the evaluator’s compound-apply +entry point. This label cannot be directly referenced in object code (since +the assembler requires that all labels referenced by the code it is assembling +be defined there), so we will add a register called compapp to the +evaluator machine to hold this entry point, and add an instruction to +initialize it: + + + (assign compapp (label compound-apply)) + ;; branches if flag is set: + (branch (label external-entry)) +read-eval-print-loop … + + +To test your code, start by defining a procedure f that calls a +procedure g. Use compile-and-go to compile the definition of +f and start the evaluator. Now, typing at the evaluator, define +g and try to call f. diff --git a/sicp/5_002e48 b/sicp/5_002e48 new file mode 100644 index 0000000..b364cf2 --- /dev/null +++ b/sicp/5_002e48 @@ -0,0 +1,24 @@ + +Exercise 5.48: The compile-and-go +interface implemented in this section is awkward, since the compiler can be +called only once (when the evaluator machine is started). Augment the +compiler-interpreter interface by providing a compile-and-run primitive +that can be called from within the explicit-control evaluator as follows: + + +;;; EC-Eval input: +(compile-and-run + '(define (factorial n) + (if (= n 1) + 1 + (* (factorial (- n 1)) n)))) + +;;; EC-Eval value: +ok + +;;; EC-Eval input: +(factorial 5) + +;;; EC-Eval value: +120 + diff --git a/sicp/5_002e49 b/sicp/5_002e49 new file mode 100644 index 0000000..24d1a33 --- /dev/null +++ b/sicp/5_002e49 @@ -0,0 +1,8 @@ + +Exercise 5.49: As an alternative to using the +explicit-control evaluator’s read-eval-print loop, design a register machine +that performs a read-compile-execute-print loop. That is, the machine should +run a loop that reads an expression, compiles it, assembles and executes the +resulting code, and prints the result. This is easy to run in our simulated +setup, since we can arrange to call the procedures compile and +assemble as “register-machine operations.” diff --git a/sicp/5_002e5 b/sicp/5_002e5 new file mode 100644 index 0000000..ba0d8f9 --- /dev/null +++ b/sicp/5_002e5 @@ -0,0 +1,5 @@ + +Exercise 5.5: Hand-simulate the factorial and +Fibonacci machines, using some nontrivial input (requiring execution of at +least one recursive call). Show the contents of the stack at each significant +point in the execution. diff --git a/sicp/5_002e50 b/sicp/5_002e50 new file mode 100644 index 0000000..59307a6 --- /dev/null +++ b/sicp/5_002e50 @@ -0,0 +1,7 @@ + +Exercise 5.50: Use the compiler to compile the +metacircular evaluator of 4.1 and run this program using the +register-machine simulator. (To compile more than one definition at a time, +you can package the definitions in a begin.) The resulting interpreter +will run very slowly because of the multiple levels of interpretation, but +getting all the details to work is an instructive exercise. diff --git a/sicp/5_002e51 b/sicp/5_002e51 new file mode 100644 index 0000000..ac9ba17 --- /dev/null +++ b/sicp/5_002e51 @@ -0,0 +1,6 @@ + +Exercise 5.51: Develop a rudimentary +implementation of Scheme in C (or some other low-level language of your choice) +by translating the explicit-control evaluator of 5.4 into C. In +order to run this code you will need to also provide appropriate +storage-allocation routines and other run-time support. diff --git a/sicp/5_002e52 b/sicp/5_002e52 new file mode 100644 index 0000000..8e962a0 --- /dev/null +++ b/sicp/5_002e52 @@ -0,0 +1,5 @@ + +Exercise 5.52: As a counterpoint to +Exercise 5.51, modify the compiler so that it compiles Scheme procedures +into sequences of C instructions. Compile the metacircular evaluator of +4.1 to produce a Scheme interpreter written in C. diff --git a/sicp/5_002e6 b/sicp/5_002e6 new file mode 100644 index 0000000..3250aea --- /dev/null +++ b/sicp/5_002e6 @@ -0,0 +1,5 @@ + +Exercise 5.6: Ben Bitdiddle observes that the +Fibonacci machine’s controller sequence has an extra save and an extra +restore, which can be removed to make a faster machine. Where are these +instructions? diff --git a/sicp/5_002e7 b/sicp/5_002e7 new file mode 100644 index 0000000..76029f2 --- /dev/null +++ b/sicp/5_002e7 @@ -0,0 +1,3 @@ + +Exercise 5.7: Use the simulator to test the +machines you designed in Exercise 5.4. diff --git a/sicp/5_002e8 b/sicp/5_002e8 new file mode 100644 index 0000000..2676315 --- /dev/null +++ b/sicp/5_002e8 @@ -0,0 +1,19 @@ + +Exercise 5.8: The following register-machine code +is ambiguous, because the label here is defined more than once: + + +start + (goto (label here)) +here + (assign a (const 3)) + (goto (label there)) +here + (assign a (const 4)) + (goto (label there)) +there + +With the simulator as written, what will the contents of register a be +when control reaches there? Modify the extract-labels procedure +so that the assembler will signal an error if the same label name is used to +indicate two different locations. diff --git a/sicp/5_002e9 b/sicp/5_002e9 new file mode 100644 index 0000000..b591bcd --- /dev/null +++ b/sicp/5_002e9 @@ -0,0 +1,5 @@ + +Exercise 5.9: The treatment of machine operations +above permits them to operate on labels as well as on constants and the +contents of registers. Modify the expression-processing procedures to enforce +the condition that operations can be used only with registers and constants.