sicp0.1.0SICP in ClojureThese are my solutions to the exercises from Structure and Interpretation of Computer Programs All feedback welcome especially if you see something I could have made clearer. I hope you find this useful and have as much fun working through these as I did dependencies
| (this space intentionally left almost blank) | |||||||||
namespaces
| ||||||||||
(ns sicp.chpt1.ex1-01) | ||||||||||
The Clojure REPL works like any other Lisp REPL | ||||||||||
When using | ||||||||||
(comment 10
12
8
3
6
'a
'b
19
false
4
16
6
16) | ||||||||||
(ns sicp.chpt1.ex1-02) | ||||||||||
Using prefix notation has 2 advantages | ||||||||||
First, your operators aren't treated differently from other functions and so you have consistency | ||||||||||
Second, precedence is obvious | ||||||||||
(/ (+ 5
4
(- 2
(- 3
(+ 6
(/ 4 5)))))
(* 3
(- 6 2)
(- 2 7))) | ||||||||||
(ns sicp.chpt1.ex1-03) | ||||||||||
(defn sqr [x] (* x x)) | ||||||||||
(defn sum-of-squares [x y] (+ (sqr x) (+ sqr y))) | ||||||||||
Each conditional step adds more information until we can make a decision | ||||||||||
| ||||||||||
(defn sum-of-greater-squares [x y z] (cond (and (< x y) (< x z)) (sum-of-squares y z) (< x y) (sum-of-squares x y) (< x z) (sum-of-squares x z) (< y z) (sum-of-squares z x) :else (sum-of-squares y x))) | ||||||||||
(ns sicp.chpt1.ex1-04) | ||||||||||
if \(b>0\) then the conditional resolves to | ||||||||||
if \(b \leq 0\) then the conditional resolves to | ||||||||||
(defn a-plus-abs-b [a b] ((if (> b 0) + -) a b)) | ||||||||||
(ns sicp.chpt1.ex1-05) | ||||||||||
In applicative order, the arguments are evaluated before being passed
into the function. The argument | ||||||||||
Normal order, resolves | ||||||||||
(ns sicp.chpt1.ex1-06) | ||||||||||
| ||||||||||
This is only in the case of applicative order evaluation | ||||||||||
(ns sicp.chpt1.ex1-07 (:use [sicp.chpt1.ex1-03 :only [sqr]])) | ||||||||||
| ||||||||||
Using a delta (even a small one) to check | ||||||||||
The suggested implementation of | ||||||||||
(defn abs [x] (if (> x 0) x (- x))) | ||||||||||
(defn good-enough?
[guess x]
(< (abs (- (sqr guess) x))
1/1000)) | ||||||||||
(defn improve
[guess x]
(/ (+ (/ x guess)
guess)
2)) | ||||||||||
(defn sqrt-iter
[guess x]
(if (good-enough? guess x)
guess
(recur (improve guess x) x))) | ||||||||||
(defn sqrt [x] (sqrt-iter 1.0 x)) | ||||||||||
(defn good-enough2?
[last-guess guess]
(< (/ (abs (- guess last-guess))
last-guess)
1/10000000)) | ||||||||||
(defn sqrt-iter2
[guess x]
(if (good-enough2? guess (improve guess x))
guess
(recur (improve guess x) x))) | ||||||||||
(defn sqrt2 [x] (sqrt-iter2 1.0 x)) | ||||||||||
(ns sicp.chpt1.ex1-08
(:use [sicp.chpt1.ex1-03 :only [sqr]]
[sicp.chpt1.ex1-07 :only [abs]])) | ||||||||||
Almost exactly like the sqrt implementation, the major change being in the implementation of | ||||||||||
(defn good-enough?
[last-guess guess]
(< (/ (abs (- guess last-guess))
last-guess)
1/10000000)) | ||||||||||
(defn improve
[guess x]
(/ (+ (/ x (sqr guess))
(* 2 guess))
3)) | ||||||||||
(defn cbrt-iter
[guess x]
(if (good-enough? guess (improve guess x))
guess
(recur (improve guess x) x))) | ||||||||||
(defn cbrt [x] (cbrt-iter 1.0 x)) | ||||||||||
(ns sicp.chpt1.ex1-09 (:refer-clojure :exclude [+])) | ||||||||||
Expanding both implementations | ||||||||||
This version is recursive | ||||||||||
| ||||||||||
(defn +
[a b]
(if (= a 0)
b
(inc (+ (dec a) b)))) | ||||||||||
This version is iterative | ||||||||||
| ||||||||||
(defn +
[a b]
(if (= a 0)
b
(recur (dec a) (inc b)))) | ||||||||||
(ns sicp.chpt1.ex1-10) | ||||||||||
The Ackermann function grows very quickly for larger values
of | ||||||||||
(defn A
[x y]
(cond (= y 0)
0
(= x 0)
(* 2 y)
(= y 1)
2
:else
(recur (- x 1)
(A x (- y 1))))) | ||||||||||
(comment (A 1 10)) | ||||||||||
| ||||||||||
(comment (A 2 4)) | ||||||||||
| ||||||||||
(comment (A 3 3)) | ||||||||||
| ||||||||||
(defn f [n] (A 0 n)) | ||||||||||
\(A(0,n) = 2n\) | ||||||||||
(defn g [n] (A 1 n)) | ||||||||||
\(A(1,n) = 2^n\) | ||||||||||
(defn h [n] (A 2 n)) | ||||||||||
\(A(2,n) = 2^{2^{2^{2...}}} n\) times | ||||||||||
\(A(2,n)\) is called tetration, this is the next hyper operator after exponentiation | ||||||||||
(ns sicp.chpt1.ex1-11) | ||||||||||
Iterative functions store state explicitly while recursive ones use the function stack | ||||||||||
(defn f-recursive
[n]
(if (< n 3)
n
(+ (f-recursive (- n 1))
(* 2 (f-recursive (- n 2)))
(* 3 (f-recursive (- n 3)))))) | ||||||||||
(defn f-iterative
[n]
(loop [a 0 b 1 c 2 i n]
(if (zero? i)
a
(recur b c (+ c (* 2 b) (* 3 a)) (dec i))))) | ||||||||||
(ns sicp.chpt1.ex1-12) | ||||||||||
The recursion can be expressed as | ||||||||||
| ||||||||||
where | ||||||||||
Returns the entry in Pascal's triangle given by the coordinates (row, column) | (defn pascal
[row column]
(if (or (= 1 column)
(= row column))
1
(+ (pascal (dec row) (dec column)) (pascal (dec row) column)))) | |||||||||
(comment
(for [x (range 1 5) y (range 1 (inc x))]
(pascal x y))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-13) | ||||||||||
The fibonacci recursion can be expressed as | ||||||||||
| ||||||||||
To prove: | ||||||||||
\(fib(n) = \frac{\phi^n - \psi^n}{\sqrt{5}}\) | ||||||||||
where | ||||||||||
| ||||||||||
Base conditions: | ||||||||||
| ||||||||||
Assume \(fib(k-1), fib(k)\) is true | ||||||||||
To prove | ||||||||||
\(fib(k+1) = fib(k) + fib(k-1)\) | ||||||||||
Substituting | ||||||||||
\(fib(k+1) = \frac{\phi^k - \psi^k}{\sqrt{5}} + \frac{\phi^(k-1) - \psi^(k-1)}{\sqrt{5}}\) | ||||||||||
Rearranging | ||||||||||
\(fib(k+1) = \frac{\phi^k(1+\frac{1}{\phi}) - \psi^k(1+\frac{1}{\psi})}{\sqrt{5}}\) | ||||||||||
This resolves to | ||||||||||
\(fib(k+1) = \frac{\phi^(k+1) - \psi^(k+1)}{\sqrt{5}}\) | ||||||||||
if and only if | ||||||||||
| ||||||||||
Which can be represented as the solution to the equation | ||||||||||
\(x^2 - x - 1 = 0\) | ||||||||||
This equation has the roots | ||||||||||
| ||||||||||
The different possible values which \(\phi\) and \(\psi\) can take | ||||||||||
\(\phi = \frac{1 - \sqrt{5}}{2}, \psi = \frac{1 - \sqrt{5}}{2}\) | ||||||||||
Invalid, \(fib(n) = 0\), \(\forall\) \(n>0\) | ||||||||||
\(\phi = \frac{1 + \sqrt{5}}{2}, \psi = \frac{1 + \sqrt{5}}{2}\) | ||||||||||
Invalid, \(fib(n) = 0\), \(\forall\) \(n>0\) | ||||||||||
\(\phi = \frac{1 - \sqrt{5}}{2}, \psi = \frac{1 + \sqrt{5}}{2}\) | ||||||||||
Invalid, \(fib(n) < 0\), \(\forall\) \(n>0\) | ||||||||||
\(\phi = \frac{1 + \sqrt{5}}{2}, \psi = \frac{1 - \sqrt{5}}{2}\) | ||||||||||
Valid, \(\forall\) \(n>0\) | ||||||||||
For larger values of \(n\), \(\psi\) becomes negligible | ||||||||||
Therefore \(fib(n)\) is approximately \(\frac{\phi^n}{\sqrt{5}}\) | ||||||||||
(ns sicp.chpt1.ex1-14 (:require [clojure.tools.trace :as t])) | ||||||||||
Each call to | ||||||||||
Since the maximum recursive depth is when | ||||||||||
(defn first-denomination
[kinds-of-coins]
(cond (= kinds-of-coins 1) 1
(= kinds-of-coins 2) 5
(= kinds-of-coins 3) 10
(= kinds-of-coins 4) 25
(= kinds-of-coins 5) 50)) | ||||||||||
(t/deftrace cc
[amount kinds-of-coins]
(cond (= amount 0) 1
(or (< amount 0) (= kinds-of-coins 0)) 0
:else
(+ (cc amount
(- kinds-of-coins 1))
(cc (- amount
(first-denomination kinds-of-coins))
kinds-of-coins)))) | ||||||||||
(defn count-change [amount] (cc amount 5)) | ||||||||||
Using | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-15) | ||||||||||
When | ||||||||||
(sine 12.15) (p (sine 4.05)) (p (p (sine 1.35))) (p (p (p (sine 0.45)))) (p (p (p (p (sine 0.15))))) (p (p (p (p (p (sine 0.05)))))) | ||||||||||
Assuming | ||||||||||
| ||||||||||
\(\frac{a}{3^n} \leq 0.1\) | ||||||||||
Solving for \(n\) | ||||||||||
\(\log _3 a \leq n\) | ||||||||||
Therefore the \(O(sine)\) is approximately \(\log _3 a\), with | ||||||||||
(ns sicp.chpt1.ex1-16) | ||||||||||
Keeping \(b^na\) constant in every iteration | ||||||||||
(defn fast-expt
([b n]
(fast-expt b n 1))
([b n a]
(cond (zero? n)
a
(even? n)
(recur (* b b) (/ n 2) a)
:else
(recur b (dec n) (* a b))))) | ||||||||||
(ns sicp.chpt1.ex1-17 (:refer-clojure :exclude [double])) | ||||||||||
For readability, double and halve | ||||||||||
(def double #(bit-shift-left % 1)) (def halve #(bit-shift-right % 1)) | ||||||||||
Recursive version of | ||||||||||
(defn fast-*
[a b]
(cond (zero? b)
0
(even? b)
(recur (double a) (halve b))
:else
(+ a (fast-* a (dec b))))) | ||||||||||
(ns sicp.chpt1.ex1-18 (:refer-clojure :exclude [double]) (:use [sicp.chpt1.ex1-17 :only [double halve]])) | ||||||||||
Iterative version of | ||||||||||
Keeping \(ba+n\) constant in every iteration | ||||||||||
(defn fast-*
([a b]
(fast-* a b 0))
([a b n]
(cond (zero? b)
n
(even? b)
(recur (double a) (halve b) n)
:else
(recur a (dec b) (+ a n))))) | ||||||||||
(ns sicp.chpt1.ex1-19) | ||||||||||
The transformation \(T _{pq}(p,q)\) is defined as | ||||||||||
| ||||||||||
Applying \(T _{pq}\) twice, | ||||||||||
\(T _{pq}(T _{pq}(a,b))\) | ||||||||||
which expands to | ||||||||||
\(T _{pq}(bq + aq + ap, bp + aq)\) | ||||||||||
finally giving us the transforms | ||||||||||
| ||||||||||
Grouping to reveal \(T _{pq}\) form, we get the new transforms | ||||||||||
| ||||||||||
So the new \(T _{p'q'}\), which is the same as applying \(T _{pq}\) twice is | ||||||||||
| ||||||||||
Filling in the gaps, here's | ||||||||||
(defn fib
([n]
(fib 1 0 0 1 n))
([a b p q n]
(cond (zero? n)
b
(even? n)
(recur a
b
(+ (* p p) (* q q))
(+ (* 2 p q) (* q q))
(/ n 2))
:else
(recur (+ (* b q) (* a q) (* a p))
(+ (* b p) (* a q))
p
q
(dec n))))) | ||||||||||
(ns sicp.chpt1.ex1-20) | ||||||||||
(defn gcd
[a b]
(if (zero? a)
b
(recur (rem b a) a))) | ||||||||||
Normal Order | ||||||||||
Evaluating | ||||||||||
each call to | ||||||||||
initial number of calls to rem is 0 | ||||||||||
| ||||||||||
+1 call to | ||||||||||
| ||||||||||
+2 calls to | ||||||||||
| ||||||||||
+4 calls to | ||||||||||
| ||||||||||
+7 calls to | ||||||||||
Finally +4 calls to | ||||||||||
Total number of calls to | ||||||||||
\(0 + 1 + 2 + 4 + 7 + 4 \rightarrow 18\) | ||||||||||
Applicative Order | ||||||||||
Evaluating | ||||||||||
Each call to | ||||||||||
initial number of calls to rem is 0 | ||||||||||
| ||||||||||
+1 call to | ||||||||||
| ||||||||||
+1 call to | ||||||||||
| ||||||||||
+1 call to | ||||||||||
| ||||||||||
Finally | ||||||||||
Total number of calls to | ||||||||||
\(0 + 1 + 1 + 1 + 1 \rightarrow 4\) | ||||||||||
(ns sicp.chpt1.ex1-21) | ||||||||||
(defn divides? [a b] (zero? (rem a b))) | ||||||||||
(defn find-smallest-divisor
([n]
(find-smallest-divisor n 2))
([n test-divisor]
(cond (> (* test-divisor test-divisor) n)
n
(divides? n test-divisor)
test-divisor
:else
(recur n (inc test-divisor))))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-22
(:use [sicp.chpt1.ex1-21 :only [divides?
find-smallest-divisor]])) | ||||||||||
(defn prime? [n] (= n (find-smallest-divisor n))) | ||||||||||
(defn timed-prime-test
[n & {:keys [prime-fn] :or {prime-fn prime?}}]
(let [start (. System (nanoTime))
is-prime (prime-fn n)]
[is-prime (/ (double (- (. System (nanoTime)) start)) 1000000.0)])) | ||||||||||
(defn find-timed-primes-between
[a b & {:keys [timed-prime-test-fn]
:or {timed-prime-test-fn timed-prime-test}}]
(letfn [(mapper [n]
(let [[is-prime time-taken] (timed-prime-test-fn n)]
[is-prime n time-taken]))]
(for [[is-prime n time-taken]
(map mapper (range (if (odd? a) a (inc a))
b
2))
:when is-prime]
[n time-taken]))) | ||||||||||
Each test was run 100,000 times before the time was recorded to account for HotSpot optimization | ||||||||||
average time taken, 0.00232 | ||||||||||
average time taken, 0.00576 | ||||||||||
average time taken, 0.01769 | ||||||||||
average time taken, 0.05313 | ||||||||||
Taking the average ratios of each order of magnitude increase \(\frac{\frac{0.00576}{0.00232} + \frac{0.01769}{0.00576} + \frac{0.05313}{0.01769}}{3} \rightarrow 2.85244\) | ||||||||||
Which is roughly equal to \(\sqrt{10}\) | ||||||||||
This supports the \(\sqrt{n}\) prediction and shows that runtime is proportional to number of steps | ||||||||||
(ns sicp.chpt1.ex1-23 (:use [sicp.chpt1.ex1-21 :only [divides?]]) (:require [sicp.chpt1.ex1-22 :as c1e22])) | ||||||||||
(defn find-smallest-divisor
([n]
(if (divides? n 2)
2
(find-smallest-divisor n 3)))
([n test-divisor]
(cond (> (* test-divisor test-divisor) n)
n
(divides? n test-divisor)
test-divisor
:else
(recur n (inc (inc test-divisor)))))) | ||||||||||
(defn prime? [n] (= n (find-smallest-divisor n))) | ||||||||||
(def timed-prime-test #(c1e22/timed-prime-test % :prime-fn prime?)) | ||||||||||
(def find-timed-primes-between #(c1e22/find-timed-primes-between %1 %2 :timed-prime-test-fn timed-prime-test)) | ||||||||||
Each test was run 100,000 times before the time was recorded to account for HotSpot optimization | ||||||||||
average time taken, 0.01859 | ||||||||||
average time taken, 0.05377 | ||||||||||
average time taken, 0.24408 | ||||||||||
average time taken, 0.52252 | ||||||||||
Taking the average ratios of each order of magnitude increase \(\frac{\frac{0.05377}{0.01859} + \frac{0.24408}{0.05377} + \frac{0.52252}{0.24408}}{3} \rightarrow 3.19084\) | ||||||||||
Which is roughly equal to \(\sqrt{10}\) | ||||||||||
Ratio of | ||||||||||
for \(n > 1000\) | ||||||||||
\(\frac{0.01859}{0.00232} \rightarrow 8.01293\) | ||||||||||
for \(n > 10000\) | ||||||||||
\(\frac{0.05377}{0.00576} \rightarrow 9.33506\) | ||||||||||
for \( n > 100000\) | ||||||||||
\(\frac{0.24408}{0.01769} \rightarrow 13.79763\) | ||||||||||
for \(n > 1000000\) | ||||||||||
\(\frac{0.52252}{0.05313} \rightarrow 9.83474\) | ||||||||||
The average is 10.24509, which is an order of magnitude worse than the original version | ||||||||||
I think this is because | ||||||||||
This is only for smaller numbers though, as the input gets larger and larger, the halving of the number of steps will dominate. Asymptotically, this algorithm should complete in half the time as the previous one | ||||||||||
(ns sicp.chpt1.ex1-24 (:use [sicp.chpt1.ex1-03 :only [sqr]]) (:require [sicp.chpt1.ex1-22 :as c1e22])) | ||||||||||
(defn expmod
[base exp m]
(cond (zero? exp)
1
(even? exp)
(rem (sqr (expmod base (/ exp 2) m)) m)
:else
(rem (* base (expmod base (dec exp) m)) m))) | ||||||||||
(defn fermat-test
[n]
(letfn [(try-it [a]
(= (expmod a n n) a))]
(try-it (+ 1 (int (rand n)))))) | ||||||||||
(defn prime?
([n]
(prime? n (int (Math/log n))))
([n times]
(if (zero? times)
true
(and (fermat-test n) (prime? n (dec times)))))) | ||||||||||
(def timed-prime-test #(c1e22/timed-prime-test % :prime-fn prime?)) | ||||||||||
(def find-timed-primes-between #(c1e22/find-timed-primes-between %1 %2 :timed-prime-test-fn timed-prime-test)) | ||||||||||
Each test was run 100,000 times before the time was recorded to account for HotSpot optimization | ||||||||||
| ||||||||||
| ||||||||||
average time taken, 0.04366 msec | ||||||||||
| ||||||||||
| ||||||||||
average time taken, 0.065654 msec | ||||||||||
| ||||||||||
| ||||||||||
average time taken, 0.08407 msec | ||||||||||
| ||||||||||
| ||||||||||
average time taken, 0.11501 msec | ||||||||||
Since the growth is \(\log _2 n\), I would have expected the time to test primes near \(10^6\) was roughly \(\log _2 1000\) times greater that testing primes near \(10^3\), roughly 10 times larger | ||||||||||
It was closer to 2.5 times larger | ||||||||||
This discrepancy is probably due to the stochastic nature of this algorithm, which can terminate earlier if the primality test fails | ||||||||||
(ns sicp.chpt1.ex1-25) | ||||||||||
Alyssa's code would work for smaller numbers but even \(10^{100}\) would overflow in most languages and would need slower, arbitrary precision arithmetic in Clojure. | ||||||||||
The | ||||||||||
(ns sicp.chpt1.ex1-26) | ||||||||||
The original | ||||||||||
by calling | ||||||||||
(ns sicp.chpt1.ex1-27
(:use [sicp.chpt1.ex1-22 :only [prime?]]
[sicp.chpt1.ex1-24 :only [expmod]])) | ||||||||||
(defn fermat-test [n a] (= (expmod a n n) a)) | ||||||||||
(defn fermat-prime?
[n]
(->> (range 2 n)
(map (partial fermat-test n))
(every? true?))) | ||||||||||
known primes and non primes
| ||||||||||
Carmichael numbers
| ||||||||||
All the Carmichael numbers in the footnote fool the fermat-prime test | ||||||||||
(ns sicp.chpt1.ex1-28
(:use [sicp.chpt1.ex1-03 :only [sqr]]
[sicp.chpt1.ex1-21 :only [divides?]]
[sicp.chpt1.ex1-24 :only [expmod]])) | ||||||||||
With lots of help from Miller Rabin primality test | ||||||||||
Returns the tuple | (defn factor-2s
[n]
(loop [t 0 d n]
(if (divides? d 2)
(recur (inc t) (/ d 2))
[t d]))) | |||||||||
(defn sqrmod [x m] (rem (sqr x) m)) | ||||||||||
(defn miller-rabin-test
[n a]
(let [[s d] (factor-2s (dec n))
x (expmod a d n)]
(if (or (= 1 x) (= (dec n) x))
true
(let [sqrs (take s (iterate #(sqrmod % n) x))]
(cond (some #(= 1 %) sqrs)
false
(some #(= (dec n) %) sqrs)
true
:else
false))))) | ||||||||||
(defn miller-rabin-prime?
[n]
(->> (range 3 (- n 2) 2)
(map (partial miller-rabin-test n))
(every? true?))) | ||||||||||
known primes and non primes | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
Carmichael numbers | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
None of the Carmichael numbers in the footnote fool the miller-rabin-prime test | ||||||||||
(ns sicp.chpt1.ex1-29) | ||||||||||
(defn cube [x] (* x x x)) | ||||||||||
(defn integral [f a b dx] (* dx (reduce + (map f (range a b dx))))) | ||||||||||
(defn simpson-integral
[f a b n]
(let [h (float (/ (- b a) n))]
(* (/ h 3)
(apply +
(f a)
(f b)
(map *
(map f (range (+ a h) b h))
(cycle [4 2])))))) | ||||||||||
Simpson's integral is more accurate than the basic integral from earlier, it approximates a better result in the same number of iterations
| ||||||||||
(ns sicp.chpt1.ex1-30) | ||||||||||
(defn sum
[term a next b]
(letfn [(iter [a result]
(if (> a b)
result
(recur (next a) (+ (term a) result))))]
(iter a 0))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-31) | ||||||||||
(defn product
[term a next b]
(letfn [(iter [a result]
(if (> a b)
result
(recur (next a) (* (term a) result))))]
(iter a 1))) | ||||||||||
(defn factorial [n] (product identity 1 inc n)) | ||||||||||
| ||||||||||
(defn pi
[n]
(letfn [(term [i]
(if (odd? i)
(/ (inc i) (+ i 2))
(/ (+ i 2) (inc i))))]
(* 4 (product term 1 inc n)))) | ||||||||||
| ||||||||||
(defn product-rec
[term a next b]
(if (> a b)
1
(* (term a)
(product-rec term (next a) next b)))) | ||||||||||
(defn factorial-rec [n] (product-rec identity 1 inc n)) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-32) | ||||||||||
(defn accumulate
[combiner null-value term a next b]
(letfn [(iter [a result]
(if (> a b)
result
(recur (next a) (combiner (term a) result))))]
(iter a null-value))) | ||||||||||
(defn sum [term a next b] (accumulate + 0 term a next b)) | ||||||||||
| ||||||||||
(defn product [term a next b] (accumulate * 1 term a next b)) | ||||||||||
| ||||||||||
(defn accumulate-rec
[combiner null-value term a next b]
(if (> a b)
null-value
(combiner (term a)
(accumulate combiner null-value term (next a) next b)))) | ||||||||||
(defn sum-rec [term a next b] (accumulate-rec + 0 term a next b)) | ||||||||||
(defn product-rec [term a next b] (accumulate-rec * 1 term a next b)) | ||||||||||
(ns sicp.chpt1.ex1-33
(:use [sicp.chpt1.ex1-20 :only [gcd]]
[sicp.chpt1.ex1-22 :only [prime?]])) | ||||||||||
(defn filtered-accumulate
[combiner null-value term a next b filter]
(letfn [(iter [a result]
(cond (> a b)
result
(filter a)
(recur (next a) (combiner (term a) result))
:else
(recur (next a) result)))]
(iter a null-value))) | ||||||||||
(defn sum-squares-of-primes
[a b]
(letfn [(square [x]
(* x x))]
(filtered-accumulate + 0 square a inc b prime?))) | ||||||||||
| ||||||||||
(defn prod-coprime
[n]
(letfn [(coprime? [x]
(= 1 (gcd x n)))]
(filtered-accumulate * 1 identity 2 inc (dec n) coprime?))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-34) | ||||||||||
Evaluating | ||||||||||
(defn f [g] (g 2)) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-35 (:use [sicp.chpt1.ex1-07 :only [abs]])) | ||||||||||
\(x \rightarrow 1 + \frac{1}{x}\) | ||||||||||
is a transformation whose fixed point is the solution to the equation | ||||||||||
\(x = 1 + \frac{1}{x}\) | ||||||||||
\(\Rightarrow x = \frac{x + 1}{x}\) | ||||||||||
\(\Rightarrow x^2 = x + 1\) | ||||||||||
which is also the definition for the golden ratio, \(\phi\) | ||||||||||
(def tolerance 1/100000) | ||||||||||
(defn fixed-point
[f first-guess]
(letfn [(close-enough? [v1 v2]
(< (abs (- v1 v2)) tolerance))
(try-it [guess]
(let [next (f guess)]
(if (close-enough? guess next)
next
(recur next))))]
(try-it first-guess))) | ||||||||||
(def phi (fixed-point (fn [x] (+ 1 (/ x))) 1)) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-36 (:use [sicp.chpt1.ex1-07 :only [abs]])) | ||||||||||
(def tolerance 1/100000) | ||||||||||
(defn fixed-point
[f first-guess]
(letfn [(close-enough? [v1 v2]
(< (abs (- v1 v2)) tolerance))
(try-it [guess i]
(println guess i)
(let [next (f guess)]
(if (close-enough? guess next)
next
(recur next (inc i)))))]
(try-it first-guess 0))) | ||||||||||
Solutions to \(x^x = 1000\) | ||||||||||
\(x \rightarrow \frac{\log 1000}{\log x}\) | ||||||||||
(defn without-damping
[]
(fixed-point (fn [x]
(/ (Math/log 1000)
(Math/log x)))
10)) | ||||||||||
without damping takes 32 steps to converge | ||||||||||
| ||||||||||
with damping | ||||||||||
\(2x \rightarrow x + \frac{\log 1000}{\log x}\) | ||||||||||
\(\Rightarrow x \rightarrow \frac{x\log x + \log 1000}{2\log x}\) | ||||||||||
(defn with-damping
[]
(fixed-point (fn [x]
(let [logx (Math/log x)]
(/ (+ (* x logx) (Math/log 1000))
(* 2 logx))))
10)) | ||||||||||
with damping takes 9 steps to converge | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-37) | ||||||||||
Recursive | ||||||||||
(defn cont-frac
([n d k]
(cont-frac n d k 1))
([n d k i]
(if (> i k)
0
(/ (n i) (+ (d i) (cont-frac n d k (inc i))))))) | ||||||||||
\(\phi \approx 1.61803399\) | ||||||||||
\(\frac{1}{\phi} \approx 0.61803398\) | ||||||||||
(defn phi-inverse
[k]
(cont-frac (constantly 1)
(constantly 1)
k)) | ||||||||||
| ||||||||||
when k is 11, we get 4 digits after the decimal point of accuracy | ||||||||||
Iterative | ||||||||||
(defn cont-frac
([n d k]
(cont-frac n d k 0))
([n d k acc]
(if (zero? k)
acc
(recur n d (dec k) (/ (n k)
(+ (d k) acc)))))) | ||||||||||
(ns sicp.chpt1.ex1-38 (:use [sicp.chpt1.ex1-37 :only [cont-frac]])) | ||||||||||
\(e \approx 2.71828183\) | ||||||||||
(defn e
[k]
(float (+ (cont-frac (constantly 1)
(fn [x]
(if (= (rem x 3) 2)
(* 2 (inc (quot x 3)))
1))
k)
2))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-39 (:use [sicp.chpt1.ex1-37 :only [cont-frac]])) | ||||||||||
(defn tan-cf
[x k]
(letfn [(numerator [i]
(if (= 1 i)
x
(- (* x x))))
(denominator [i]
(dec (* 2 i)))]
(float (cont-frac numerator denominator k)))) | ||||||||||
\(\tan 0 = 0\) | ||||||||||
| ||||||||||
\(\tan \frac{\pi}{4} = 1\) | ||||||||||
| ||||||||||
\(\tan \frac{\pi}{2} = \infty\) | ||||||||||
| ||||||||||
\(\tan \frac{3\pi}{4} = -1\) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-40 (:use [sicp.chpt1.ex1-35 :only [fixed-point]])) | ||||||||||
(def dx 1/100000) | ||||||||||
(defn deriv
[g]
(fn [x]
(/ (- (g (+ x dx)) (g x))
dx))) | ||||||||||
(defn newton-transform
[g]
(fn [x]
(- x (/ (g x) ((deriv g) x))))) | ||||||||||
(defn newton-method [g guess] (fixed-point (newton-transform g) guess)) | ||||||||||
(defn cubic
[a b c]
(fn [x]
(+ (* x x x)
(* a x x)
(* b x)
c))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-41 (:refer-clojure :exclude [double])) | ||||||||||
(defn double
[f]
(fn [x]
(f (f x)))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-42) | ||||||||||
(defn compose
[f g]
(fn [x]
(f (g x)))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-43) | ||||||||||
(defn repeated
[f n]
(loop [g f i n]
(cond (= 1 i)
g
(even? i)
(recur (comp g g) (/ i 2))
:else
(recur (comp f g) (dec i))))) | ||||||||||
| ||||||||||
(ns sicp.chpt1.ex1-44 (:use [sicp.chpt1.ex1-43 :only [repeated]])) | ||||||||||
(def dx 1/100000) | ||||||||||
(defn smooth
[f]
(fn [x]
(/ (+ (f (- x dx))
(f x)
(f (+ x dx)))
3))) | ||||||||||
(defn n-fold-smooth [f n] ((repeated smooth n) f)) | ||||||||||
(ns sicp.chpt1.ex1-45
(:use [sicp.chpt1.ex1-07 :only [abs]]
[sicp.chpt1.ex1-16 :only [fast-expt]]
[sicp.chpt1.ex1-35 :only [fixed-point]]
[sicp.chpt1.ex1-43 :only [repeated]])) | ||||||||||
(def tolerance 1/100000) | ||||||||||
(defn fixed-point-limit
[f first-guess limit]
(letfn [(close-enough? [v1 v2]
(< (abs (- v1 v2)) tolerance))
(try-it [guess i]
(let [next (f guess)]
(if (or (zero? i) (close-enough? guess next))
[next (- limit i)]
(recur next (dec i)))))]
(try-it first-guess limit))) | ||||||||||
(defn fixed-point-of-transform-limit [g transform guess limit] (fixed-point-limit (transform g) guess limit)) | ||||||||||
(defn fixed-point-of-transform [g transform guess] (fixed-point (transform g) guess)) | ||||||||||
(defn average-damp
[f]
(fn [x]
(/ (+ (f x) x) 2))) | ||||||||||
(defn nth-root-fixed-point
[y n]
(fn [x]
(/ y (fast-expt x (dec n))))) | ||||||||||
| ||||||||||
Steps Reached [100] | ||||||||||
| ||||||||||
Steps Reached [7] | ||||||||||
| ||||||||||
Steps Reached [100] | ||||||||||
| ||||||||||
Steps Reached [12] | ||||||||||
| ||||||||||
Steps Reached [100] | ||||||||||
| ||||||||||
Steps Reached [37] | ||||||||||
In conculusion, average damping of at least \(\log _2 n\) is needed to converge the fixed point for the nth root | ||||||||||
(defn nth-root
[x n]
(fixed-point-of-transform (nth-root-fixed-point x n)
(repeated average-damp (int (/ (Math/log n)
(Math/log 2))))
1.0)) | ||||||||||
(ns sicp.chpt1.ex1-46) | ||||||||||
(defn iterative-improve
[good-enough? improve-guess]
(fn [guess]
(if (good-enough? guess)
guess
(recur (improve-guess guess))))) | ||||||||||
(def tolerance 1/10000) | ||||||||||
(defn sqrt
[x]
(letfn [(good-enough?
[guess]
(< (Math/abs (- (* guess guess) x)) tolerance))
(improve-guess
[guess]
(/ (+ (/ x guess) guess) 2))]
((iterative-improve good-enough? improve-guess) 1.0))) | ||||||||||
(defn fixed-point
[f]
(letfn [(good-enough?
[guess]
(< (Math/abs (- (f guess) guess)) tolerance))
(improve-guess
[guess]
(f guess))]
(iterative-improve good-enough? improve-guess))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-01 (:use [sicp.chpt1.ex1-20 :only [gcd]])) | ||||||||||
Using Clojure's records instead of cons cells as a means of combination | ||||||||||
(defrecord Rational [n d]) | ||||||||||
(defn make-rat
[n d]
(let [div (Math/abs (gcd n d))
n-div (if (or (and (pos? n) (neg? d))
(and (neg? n) (neg? d)))
(- div)
div)]
(Rational. (/ n n-div) (/ d n-div)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-02) | ||||||||||
(defrecord Point [x y]) | ||||||||||
(defn make-point [x y] (Point. x y)) | ||||||||||
(defn x-point [p] (.x p)) | ||||||||||
(defn y-point [p] (.y p)) | ||||||||||
(defrecord Segment [p1 p2]) | ||||||||||
(defn make-segment [p1 p2] (Segment. p1 p2)) | ||||||||||
(defn start-segment [s] (.p1 s)) | ||||||||||
(defn end-segment [s] (.p2 s)) | ||||||||||
(defn midpoint
[s]
(let [p1 (start-segment s)
p2 (end-segment s)
x1 (x-point p1)
y1 (y-point p1)
x2 (x-point p2)
y2 (y-point p2)]
(make-point (/ (+ x1 x2) 2)
(/ (+ y1 y2) 2)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-03
(:use [sicp.chpt2.ex2-02 :only [make-point
x-point
y-point
make-segment
start-segment
end-segment]])) | ||||||||||
For the sake of simplicity, assume the sides of the rectangle are always perpendicular to the coordinate axes | ||||||||||
Here we're storing only the diagonal, for a general rectangle definition, the angle between a side and an axis would also be needed. | ||||||||||
(defrecord Rectangle [diag]) | ||||||||||
(defn make-rectangle [diag] (Rectangle. diag)) | ||||||||||
Calculate the other points whenever | ||||||||||
(defn rectangle-points
[rect]
(let [diag (.diag rect)
a (start-segment diag)
c (end-segment diag)
b (make-point (x-point a)
(y-point c))
d (make-point (x-point c)
(y-point a))]
[a b c d])) | ||||||||||
| ||||||||||
(defn distance
[p1 p2]
(letfn [(sqr [x]
(* x x))]
(Math/sqrt (+ (sqr (- (y-point p2)
(y-point p1)))
(sqr (- (x-point p2)
(x-point p1))))))) | ||||||||||
(defn perimeter
[rect]
(let [[a b c d] (rectangle-points rect)]
(* 2 (+ (distance a b)
(distance b c))))) | ||||||||||
(defn area
[rect]
(let [[a b c] (rectangle-points rect)]
(* (distance a b)
(distance b c)))) | ||||||||||
| ||||||||||
Changing how rectangles are stored to store all 4 points | ||||||||||
(defrecord Rectangle [a b c d]) | ||||||||||
(defn make-rectangle
[diag]
(let [a (start-segment diag)
c (end-segment diag)
b (make-point (x-point a)
(y-point c))
d (make-point (x-point c)
(y-point a))]
(Rectangle. a b c d))) | ||||||||||
All the points are already stored, they just need to be returned | ||||||||||
(defn rectangle-points [rect] [(.a rect) (.b rect) (.c rect) (.d rect)]) | ||||||||||
| ||||||||||
The trade off here is between memory and performance. | ||||||||||
The diagonal rectangle stored the minimum required data but needed to calculate all
4 points everytime | ||||||||||
The point approach calculated all 4 points just once but needed to store twice the number of points. | ||||||||||
(ns sicp.chpt2.ex2-04 (:refer-clojure :exclude [cons])) | ||||||||||
(defn cons [x y] (fn [m] (m x y))) | ||||||||||
(defn car [z] (z (fn [x y] x))) | ||||||||||
The corresponding definition of cdr | ||||||||||
(defn cdr [z] (z (fn [x y] y))) | ||||||||||
| ||||||||||
| ||||||||||
Using the substitution model | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-05 (:refer-clojure :exclude [cons])) | ||||||||||
Since both 2 and 3 are prime, \(2^{a}3^{b}\) is unique to the pair (a, b) | ||||||||||
(defn cons
[x y]
(int (* (Math/pow 2 x)
(Math/pow 3 y)))) | ||||||||||
| ||||||||||
(defn- d-component
[n d]
(loop [x n acc 0]
(if (zero? (rem x d))
(recur (/ x d) (inc acc))
acc))) | ||||||||||
(defn car [z] (d-component z 2)) | ||||||||||
(defn cdr [z] (d-component z 3)) | ||||||||||
| ||||||||||
| ||||||||||
Using the substitution model | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-06) | ||||||||||
An integer \(n\) corresponds to n applications of a function | ||||||||||
(defn church->int [n] ((n inc) 0)) | ||||||||||
(def zero
(fn [f]
(fn [x]
x))) | ||||||||||
(defn add1
[n]
(fn [f]
(fn [x]
(f ((n f) x))))) | ||||||||||
(def one
(fn [f]
(fn [x]
(f x)))) | ||||||||||
(def two
(fn [f]
(fn [x]
(f (f x))))) | ||||||||||
Addition is simply function composition | ||||||||||
(defn add
[a b]
(fn [f]
(comp (a f) (b f)))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-07) | ||||||||||
(defrecord Interval [lb ub]) | ||||||||||
(defn make-interval [lb ub] (Interval. lb ub)) | ||||||||||
(defn lower-bound [i] (.lb i)) | ||||||||||
(defn upper-bound [i] (.ub i)) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-08
(:use [sicp.chpt2.ex2-07 :only [make-interval
lower-bound
upper-bound]])) | ||||||||||
(defn add-interval
[x y]
(make-interval (+ (lower-bound x)
(lower-bound y))
(+ (upper-bound x)
(upper-bound y)))) | ||||||||||
| ||||||||||
(defn sub-interval
[x y]
(let [a (- (lower-bound x)
(lower-bound y))
b (- (upper-bound x)
(upper-bound y))]
(if (< a b)
(make-interval a b)
(make-interval b a)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-09
(:use [sicp.chpt2.ex2-07 :only [make-interval
lower-bound
upper-bound]]
[sicp.chpt2.ex2-08 :only [add-interval
sub-interval]])) | ||||||||||
(defn mul-interval
[x y]
(let [p1 (* (lower-bound x) (lower-bound y))
p2 (* (lower-bound x) (upper-bound y))
p3 (* (upper-bound x) (lower-bound y))
p4 (* (upper-bound x) (upper-bound y))]
(make-interval (min p1 p2 p3 p4)
(max p1 p2 p3 p4)))) | ||||||||||
(defn div-interval
[x y]
(mul-interval x
(make-interval (/ (lower-bound y))
(/ (upper-bound y))))) | ||||||||||
(defn width
[x]
(/ (- (upper-bound x)
(lower-bound x))
2)) | ||||||||||
Aliasing | ||||||||||
let | ||||||||||
| ||||||||||
let z be | ||||||||||
| ||||||||||
Expanding \(width(z)\) | ||||||||||
\(\Rightarrow \frac{ub(z) - lb(z)}{2}\) | ||||||||||
\(\Rightarrow \frac{(ub(x) + ub(y)) - (lb(x) + lb(y))}{2}\) | ||||||||||
\(\Rightarrow \frac{ub(x) + ub(y) - lb(x) - lb(y)}{2}\) | ||||||||||
\(\Rightarrow \frac{ub(x) - lb(x) + ub(y) - lb(y)}{2}\) | ||||||||||
\(\Rightarrow \frac{ub(x) - lb(x)}{2} + \frac{ub(y) - lb(y)}{2}\) | ||||||||||
\(\Rightarrow width(x) + width(y)\) | ||||||||||
Therefore | ||||||||||
\(width(x + y) \Rightarrow width(x) + width(y)\) | ||||||||||
Similarly for subtraction | ||||||||||
For multiplication or division, the max and min of the products of the bounds are taken, therefore the width of the multiplication or division of 2 intervals isn't a multiplication or division of the widths of the bounds of the intervals | ||||||||||
(ns sicp.chpt2.ex2-10
(:use [sicp.chpt2.ex2-07 :only [make-interval
lower-bound
upper-bound]]
[sicp.chpt2.ex2-09 :only [mul-interval]])) | ||||||||||
(defn div-interval
[x y]
(letfn [(spans-zero?
[i]
(and (<= (lower-bound i) 0)
(<= 0 (upper-bound i))))]
(if (spans-zero? y)
(throw (ArithmeticException. "Divide by zero"))
(mul-interval x
(make-interval (/ (lower-bound y))
(/ (upper-bound y))))))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-11
(:use [sicp.chpt2.ex2-07 :only [make-interval
lower-bound
upper-bound]])) | ||||||||||
(defn mul-interval
[x y]
(let [lbx (lower-bound x)
lby (lower-bound y)
ubx (upper-bound x)
uby (upper-bound y)]
(cond (and (pos? lbx) (pos? lby))
(make-interval (* lbx lby) (* ubx uby))
(and (neg? lbx) (neg? lby) (neg? ubx) (neg? uby))
(make-interval (* ubx uby) (* lbx lby))
(and (pos? lbx) (neg? lby) (pos? ubx) (neg? uby))
(make-interval (* ubx lby) (* lbx uby))
(and (neg? lbx) (pos? lby) (neg? ubx) (pos? uby))
(make-interval (* lbx uby) (* ubx lby))
(and (pos? lbx) (neg? lby) (pos? ubx) (pos? uby))
(make-interval (* ubx lby) (* ubx uby))
(and (neg? lbx) (pos? lby) (pos? ubx) (pos? uby))
(make-interval (* lbx uby) (* ubx uby))
(and (neg? lbx) (neg? lby) (neg? ubx) (pos? uby))
(make-interval (* lbx uby) (* lbx lby))
(and (neg? lbx) (neg? lby) (pos? ubx) (neg? uby))
(make-interval (* ubx lby) (* lbx uby))
:else
(let [l1 (* lbx uby)
l2 (* ubx lby)
u1 (* lbx lby)
u2 (* ubx uby)]
(make-interval (if (< l1 l2) l1 l2)
(if (< u1 u2) u2 u1)))))) | ||||||||||
(ns sicp.chpt2.ex2-12
(:use [sicp.chpt2.ex2-07 :only [make-interval
lower-bound
upper-bound]])) | ||||||||||
(defn make-center-percent
[c p]
(let [width (Math/abs (* (/ p 100.0) c))]
(make-interval (- c width)
(+ c width)))) | ||||||||||
(defn center
[i]
(/ (+ (lower-bound i)
(upper-bound i))
2)) | ||||||||||
(defn percent
[i]
(let [c (center i)]
(Math/abs (* (/ (- (upper-bound i) c)
c)
100.0)))) | ||||||||||
(defn print-interval
[i]
(format "(%1.2f, %1.2f%%)"
(double (center i))
(double (percent i)))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-13
(:use [sicp.chpt2.ex2-12 :only [make-center-percent
center
percent]])) | ||||||||||
Let \(x \pm a\) and \(y \pm b\) be 2 intervals | ||||||||||
\((x \pm a) \times (y \pm b)\) | ||||||||||
In [lower bound, upper bound] form | ||||||||||
\(\Rightarrow [x (1 - \frac{a}{100}), x(1 + \frac{a}{100})] \times [y (1 - \frac{b}{100}), y(1 + \frac{b}{100})]\) | ||||||||||
Assuming both have positive lower-bounds | ||||||||||
\(\Rightarrow [xy(1 - \frac{a}{100})(1 - \frac{b}{100}), xy(1 + \frac{a}{100})(1 + \frac{b}{100})]\) | ||||||||||
\(\Rightarrow [xy(1 - \frac{a}{100} - \frac{b}{100} + \frac{ab}{10000}), xy(1 + \frac{a}{100} + \frac{b}{100} + \frac{ab}{10000})]\) | ||||||||||
For small percentage tolerances, \(\frac{ab}{10000} \rightarrow 0\) | ||||||||||
\(\Rightarrow [xy(1 - \frac{a + b}{100}), xy(1 + \frac{a+b}{100})]\) | ||||||||||
\(\Rightarrow xy \pm (a+b)\) | ||||||||||
(defn mul-interval
[i1 i2]
(make-center-percent (* (center i1) (center i2))
(+ (percent i1) (percent i2)))) | ||||||||||
(ns sicp.chpt2.ex2-14
(:use [sicp.chpt2.ex2-07 :only [make-interval]]
[sicp.chpt2.ex2-08 :only [add-interval]]
[sicp.chpt2.ex2-09 :only [mul-interval]]
[sicp.chpt2.ex2-10 :only [div-interval]]
[sicp.chpt2.ex2-12 :only [make-center-percent
print-interval]])) | ||||||||||
(defn par1
[r1 r2]
(div-interval (mul-interval r1 r2)
(add-interval r1 r2))) | ||||||||||
(defn par2
[r1 r2]
(let [one (make-interval 1 1)]
(div-interval one
(add-interval (div-interval one r1)
(div-interval one r2))))) | ||||||||||
| ||||||||||
| ||||||||||
The error increases by 1% with every multiplication | ||||||||||
| ||||||||||
| ||||||||||
The error increases by 0.5% with every addition | ||||||||||
| ||||||||||
The formula \(\frac{R1R2}{R1+R2}\) results in a higher error than \(\frac{1}{R1} + \frac{1}{R2}\) | ||||||||||
Every operation with intervals, even if algebraicly a no-op increases the error | ||||||||||
The problem here is that identical intervals don't cancel themselves, ie. \(\frac{x}{x} \neq 1\) | ||||||||||
(ns sicp.chpt2.ex2-15) | ||||||||||
The formula for calculating resitances in parallel is \(\frac{1}{R1} + \frac{1}{R2}\) | ||||||||||
Simplifying the formula by one step we get \(\frac{1}{\frac{R2 + R1}{R1R2}}\) | ||||||||||
This makes the assumption that \(\frac{R1}{R1}\) and \(\frac{R2}{R2}\) are 1 | ||||||||||
But with interval arithmetic, they aren't exactly 1 | ||||||||||
Therefore the simplification is not possible | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-16) | ||||||||||
Regular algebra assumes that identities always have a fixed value, that's why identical identites can cancel each other out perfectly | ||||||||||
With intervals that's not the case. The value is not precise and so normal algebraic simplifications can't work. | ||||||||||
I don't think it is possible to design an interval-arithmetic library that does not have this problem. | ||||||||||
What we can do is to express our equations with no standard algebraic assumptions like \(\frac{n}{n}, n \neq 0 \rightarrow 1\). | ||||||||||
This will minimize the error | ||||||||||
(ns sicp.chpt2.ex2-17) | ||||||||||
(defn last-pair
[l]
(if (empty? (rest l))
l
(recur (rest l)))) | ||||||||||
Other Implementations | ||||||||||
(def last-pair (comp list last)) | ||||||||||
(ns sicp.chpt2.ex2-18 (:refer-clojure :exclude [reverse])) | ||||||||||
Recursive | ||||||||||
(defn reverse
[l]
(if (empty? l)
[]
(conj (reverse (rest l)) (first l)))) | ||||||||||
Iterative | ||||||||||
(defn reverse
([l]
(reverse l ()))
([l acc]
(if (empty? l)
acc
(recur (rest l) (conj acc (first l)))))) | ||||||||||
Other Implementations | ||||||||||
(def reverse clojure.core/reverse) | ||||||||||
(ns sicp.chpt2.ex2-19) | ||||||||||
(def no-more? empty?) | ||||||||||
(def except-first-denomination rest) | ||||||||||
(def first-denomination first) | ||||||||||
(defn 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)))) | ||||||||||
(def us-coins [50 25 10 5 1]) | ||||||||||
(def uk-coins [100 50 20 10 5 2 1 0.5]) | ||||||||||
No the initial ordering doesn't matter as long as the same ordering is maintained throughout subsequent calls | ||||||||||
The problem is defined in terms of 2 sub problems | ||||||||||
The first problem calculates the number of ways to count change without one of the denominations but the same amount | ||||||||||
The second problem calculates the number of ways to count change with the same denomination list but with the amount reduced by the first value in the demoniation list | ||||||||||
Both functions make the assumption that the initial order doesn't change and track the state of the system that way. | ||||||||||
(ns sicp.chpt2.ex2-20) | ||||||||||
Recursive | ||||||||||
(defn same-parity
[x & xs]
(cond (empty? xs)
()
(= (rem x 2) (rem (first xs) 2))
(conj (apply same-parity x (rest xs))
(first xs))
:else
(apply same-parity x (rest xs)))) | ||||||||||
Iterative | ||||||||||
(defn- same-parity-iter
[x xs acc]
(cond (empty? xs)
acc
(= (rem x 2) (rem (first xs) 2))
(recur x (rest xs) (conj acc (first xs)))
:else
(recur x (rest xs) acc))) | ||||||||||
(defn same-parity [x & xs] (same-parity-iter x xs [])) | ||||||||||
Other Implementations | ||||||||||
(defn same-parity [x & xs] (filter (if (even? x) even? odd?) xs)) | ||||||||||
(ns sicp.chpt2.ex2-21) | ||||||||||
Recursive | ||||||||||
(defn square-list
[items]
(if (empty? items)
items
(conj (square-list (rest items))
(#(* % %) (first items))))) | ||||||||||
Sequential | ||||||||||
(defn square-list [items] (map #(* % %) items)) | ||||||||||
in Clojure, the sequence operators (like | ||||||||||
(ns sicp.chpt2.ex2-22) | ||||||||||
using loop instead of an anonymous fn | ||||||||||
(defn square-list
[items]
(loop [things items answer ()]
(if (empty? things)
answer
(recur (rest things)
(conj answer
(#(* % %) (first things))))))) | ||||||||||
Clojure (and most lisps, append to the front of a list when consing,
the list is processed in order but each successive result is appended
to the start of | ||||||||||
(defn square-list
[items]
(loop [things items answer ()]
(if (empty? things)
answer
(recur (rest things)
(conj (#(* % %) (first things))
answer))))) | ||||||||||
Clojure doesn't have a concept of pairs, you can only | ||||||||||
Solutions | ||||||||||
There are a few ways to get around this | ||||||||||
Using | ||||||||||
Another solution is to use Clojure's | ||||||||||
| ||||||||||
| ||||||||||
initializing answer with | ||||||||||
(ns sicp.chpt2.ex2-23) | ||||||||||
(defn for-each
([f coll]
(for-each f coll nil))
([f coll return]
(loop [things coll]
(if (empty? things)
return
(do (f (first things))
(recur (rest things))))))) | ||||||||||
| ||||||||||
Other Implementations | ||||||||||
(defn for-each
([f coll]
(for-each f coll nil))
([f coll return]
(doseq [x coll]
(f x))
return)) | ||||||||||
(ns sicp.chpt2.ex2-24) | ||||||||||
Interpreter Output | ||||||||||
| ||||||||||
Box and Pointer | ||||||||||
| ||||||||||
Tree | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-25) | ||||||||||
| ||||||||||
(let [s [1 3 [5 7] 9]] (first (rest (first (rest (rest s)))))) | ||||||||||
| ||||||||||
(let [s [[7]]] (first (first s))) | ||||||||||
| ||||||||||
(let [s [1 [2 [3 [4 [5 [6 7]]]]]]] (first (rest (first (rest (first (rest (first (rest (first (rest (first (rest s))))))))))))) | ||||||||||
(ns sicp.chpt2.ex2-26) | ||||||||||
(def x (list 1 2 3)) (def y (list 4 5 6)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-27) | ||||||||||
(defn deep-reverse
[x]
(cond (empty? x)
[]
(coll? (first x))
(conj (deep-reverse (rest x))
(deep-reverse (first x)))
:else
(conj (deep-reverse (rest x))
(first x)))) | ||||||||||
(def x (list (list 1 2) (list 3 4))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-28) | ||||||||||
(defn fringe
[l]
(cond (empty? l)
()
(coll? (first l))
(concat (fringe (first l))
(fringe (rest l)))
:else
(conj (fringe (rest l)) (first l)))) | ||||||||||
(def x (list (list 1 2) (list 3 4))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-29) | ||||||||||
(defn make-mobile [left right] (list left right)) | ||||||||||
(defn make-branch [length structure] (list length structure)) | ||||||||||
a. selectors | ||||||||||
(defn left-branch [mobile] (first mobile)) | ||||||||||
(defn right-branch [mobile] (second mobile)) | ||||||||||
(defn branch-length [branch] (first branch)) | ||||||||||
(defn branch-structure [branch] (second branch)) | ||||||||||
b. mobile weights | ||||||||||
(defn mobile? [structure] (not (number? structure))) | ||||||||||
(declare total-weight) | ||||||||||
(defn branch-weight
[branch]
(if (mobile? (branch-structure branch))
(total-weight (branch-structure branch))
(branch-structure branch))) | ||||||||||
(defn total-weight
[mobile]
(+ (branch-weight (left-branch mobile))
(branch-weight (right-branch mobile)))) | ||||||||||
| ||||||||||
c. balanced? | ||||||||||
(defn torque
[branch]
(* (branch-length branch)
(branch-weight branch))) | ||||||||||
(declare balanced?) | ||||||||||
(defn balanced-branch?
[branch]
(if (mobile? (branch-structure branch))
(balanced? (branch-structure branch))
true)) | ||||||||||
(defn balanced?
[mobile]
(and (= (torque (left-branch mobile))
(torque (right-branch mobile)))
(balanced-branch? (left-branch mobile))
(balanced-branch? (right-branch mobile)))) | ||||||||||
| ||||||||||
| ||||||||||
d. abstraction | ||||||||||
since Clojure doesn't have | ||||||||||
(defn make-mobile [left right] (vector left right)) | ||||||||||
(defn make-branch [length structure] (vector length structure)) | ||||||||||
since a | ||||||||||
(ns sicp.chpt2.ex2-30) | ||||||||||
directly | ||||||||||
(defn square-tree
[tree]
(cond (not (coll? tree))
(* tree tree)
(empty? tree)
()
:else
(conj (square-tree (rest tree))
(square-tree (first tree))))) | ||||||||||
using map | ||||||||||
(defn square-tree
[tree]
(map (fn [subtree]
(if (coll? subtree)
(square-tree subtree)
(* subtree subtree)))
tree)) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-31) | ||||||||||
(defn tree-map
[f tree]
(map (fn [subtree]
(if (coll? subtree)
(tree-map f subtree)
(f subtree)))
tree)) | ||||||||||
(defn square-tree [tree] (tree-map #(* % %) tree)) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-32) | ||||||||||
(defn subsets
[s]
(if (empty? s)
[()]
(let [rst (subsets (rest s))]
(concat rst (map #(conj % (first s)) rst))))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-33 (:refer-clojure :exclude [map])) | ||||||||||
Clojure comes with a built in | ||||||||||
accumulate | ||||||||||
(defn accumulate
[op initial sequence]
(if (empty? sequence)
initial
(op (first sequence)
(accumulate op initial (rest sequence))))) | ||||||||||
map | ||||||||||
(defn map [p sequence] (accumulate (fn [x y] (conj y (p x))) () sequence)) | ||||||||||
| ||||||||||
append | ||||||||||
(defn append [seq1 seq2] (accumulate cons seq2 seq1)) | ||||||||||
| ||||||||||
length | ||||||||||
(defn length [sequence] (accumulate (fn [_ n] (inc n)) 0 sequence)) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-34 (:use [sicp.chpt2.ex2-33 :only [accumulate]])) | ||||||||||
(defn horner-eval
[x coefficient-sequence]
(accumulate (fn [this-coeff higher-terms] (+ this-coeff (* x higher-terms)))
0
coefficient-sequence)) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-35 (:use [sicp.chpt2.ex2-33 :only [accumulate]])) | ||||||||||
(declare count-leaves) | ||||||||||
(defn count-coll
[x]
(if (coll? x)
(count-leaves x)
1)) | ||||||||||
(defn count-leaves [t] (accumulate + 0 (map count-coll t))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-36 (:use [sicp.chpt2.ex2-33 :only [accumulate]])) | ||||||||||
(defn accumulate-n
[op init seqs]
(if (empty? (first seqs))
()
(cons (accumulate op init (map first seqs))
(accumulate-n op init (map rest seqs))))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-37 (:use [sicp.chpt2.ex2-33 :only [accumulate]]) (:use [sicp.chpt2.ex2-36 :only [accumulate-n]])) | ||||||||||
dot-product | ||||||||||
(defn dot-product [v w] (accumulate + 0 (map * v w))) | ||||||||||
| ||||||||||
matrix-*-vector | ||||||||||
(defn matrix-*-vector
[m v]
(map (fn [row]
(dot-product v row))
m)) | ||||||||||
| ||||||||||
transpose | ||||||||||
(defn transpose [mat] (accumulate-n cons () mat)) | ||||||||||
| ||||||||||
matrix-*-matrix | ||||||||||
(defn matrix-*-matrix
[m n]
(let [cols (transpose n)]
(map (fn [row]
(matrix-*-vector cols row))
m))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-38) | ||||||||||
Clojure's | ||||||||||
(defn fold-left
[op initial sequence]
(loop [acc initial s sequence]
(if (empty? s)
acc
(recur (op acc (first s)) (rest s))))) | ||||||||||
(defn fold-right
[op initial sequence]
(if (empty? sequence)
initial
(op (first sequence)
(fold-right op initial (rest sequence))))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
for | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-39 (:refer-clojure :exclude [reverse]) (:use [sicp.chpt2.ex2-38 :only [fold-left fold-right]])) | ||||||||||
with | ||||||||||
(defn reverse [sequence] (fold-left (fn [x y] (cons y x)) () sequence)) | ||||||||||
with | ||||||||||
(defn reverse [sequence] (fold-right (fn [x y] (concat y (list x))) () sequence)) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-40 (:use [sicp.chpt1.ex1-22 :only [prime?]])) | ||||||||||
Clojure's | ||||||||||
(defn unique-pairs
[n]
(mapcat (fn [i]
(map (fn [j] [i j])
(range 1 i)))
(range 2 (inc n)))) | ||||||||||
| ||||||||||
(defn make-pair-sum [[i j]] [i j (+ i j)]) | ||||||||||
(defn prime-sum? [[_ _ sum]] (prime? sum)) | ||||||||||
(defn prime-sum-pairs
[n]
(->> (unique-pairs n)
(map make-pair-sum)
(filter prime-sum?))) | ||||||||||
| ||||||||||
| ||||||||||
(defn unique-pairs
[n]
(for [i (range 2 (inc n))
j (range 1 i)]
[i j])) | ||||||||||
| ||||||||||
(defn prime-sum-pairs
[n]
(for [i (range 2 (inc n))
j (range 1 i)
:when (prime? (+ i j))]
[i j (+ i j)])) | ||||||||||
(ns sicp.chpt2.ex2-41) | ||||||||||
using | ||||||||||
(defn ordered-triples
[n]
(mapcat (fn [i]
(mapcat (fn [j]
(map (fn [k]
[i j k])
(range 1 (inc n))))
(range 1 (inc n))))
(range 1 (inc n)))) | ||||||||||
(defn filtered-triples
[n s]
(letfn [(sum-s? [[i j k]]
(= s (+ i j k)))]
(filter sum-s? (ordered-triples n)))) | ||||||||||
| ||||||||||
using | ||||||||||
(defn filtered-triples
[n s]
(for [i (range 1 (inc n))
j (range 1 (inc n))
k (range 1 (inc n))
:when (= s (+ i j k))]
[i j k])) | ||||||||||
(ns sicp.chpt2.ex2-42) | ||||||||||
scaffolding | ||||||||||
(defrecord Position [col row]) | ||||||||||
(defn make-position [col row] (Position. col row)) | ||||||||||
(defn column-value [position] (.col position)) | ||||||||||
(defn row-value [position] (.row position)) | ||||||||||
implementation | ||||||||||
(def empty-board ()) | ||||||||||
(defn adjoin-position [row k positions] (conj positions (make-position (dec k) row))) | ||||||||||
(defn attacks?
[p1 p2]
(or (= (row-value p1) (row-value p2))
(= (column-value p1) (column-value p2))
(= (- (row-value p1) (column-value p1)) ; along right diagonal
(- (row-value p2) (column-value p2)))
(= (+ (row-value p1) (column-value p1)) ; along left diagonal
(+ (row-value p2) (column-value p2))))) | ||||||||||
(defn safe?
[k positions]
(not (some (partial attacks? (first positions))
(rest positions)))) | ||||||||||
(defn queens
[board-size]
(letfn [(queen-cols
[k]
(if (zero? k)
[empty-board]
(filter (fn [positions] (safe? k positions))
(mapcat
(fn [rest-of-queens]
(map (fn [new-row]
(adjoin-position new-row k rest-of-queens))
(range board-size)))
(queen-cols (dec k))))))]
(queen-cols board-size))) | ||||||||||
using | ||||||||||
(defn queens
[board-size]
(letfn [(queen-cols
[k]
(if (zero? k)
[empty-board]
(for [positions (queen-cols (dec k))
new-row (range board-size)
:when (safe? k (adjoin-position new-row k positions))]
(adjoin-position new-row k positions))))]
(queen-cols board-size))) | ||||||||||
ascii art | ||||||||||
(defn place-pieces
[board-size positions]
(reduce (fn [board position]
(assoc-in board [(row-value position) (column-value position)] :q))
(vec (repeat board-size (vec (repeat board-size :-))))
positions)) | ||||||||||
(defn print-board
[board]
(doseq [row board]
(println row))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-43) | ||||||||||
Both algorithms are recursive | ||||||||||
The first algorithm is linearly recursive, every iteration calls | ||||||||||
The second algorithm is tree recursive, with | ||||||||||
If the first algorithm runs in time \(T\), the second algorithm will complete after time \(T^T\) | ||||||||||
(ns sicp.chpt2.ex2-44
(:use [sicp.chpt2.ex2-51 :only [below beside]]
[sicp.chpt2.ex2-49 :only [wave-painter
identity-frame
render]])) | ||||||||||
The first few functions defined in this section refer
to functions defined later (like | ||||||||||
(defn up-split
[painter n]
(if (zero? n)
painter
(let [smaller (up-split painter (dec n))]
(below painter (beside smaller smaller))))) | ||||||||||
Don't worry too much about the examples right now, just evaluating them to understand what they do is enough | ||||||||||
the basic | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-45
(:use [sicp.chpt2.ex2-51 :only [beside below]]
[sicp.chpt2.ex2-49 :only [wave-painter
identity-frame
render]])) | ||||||||||
(defn split
[major-op minor-op]
(fn [painter n]
(if (zero? n)
painter
(let [smaller ((split major-op minor-op) painter (dec n))]
(major-op painter (minor-op smaller smaller)))))) | ||||||||||
(def right-split (split beside below)) (def up-split (split below beside)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-46) | ||||||||||
(defrecord Vector [x y]) | ||||||||||
(defn make-vect [x y] (Vector. x y)) | ||||||||||
(defn xcor-vect [v] (.x v)) | ||||||||||
(defn ycor-vect [v] (.y v)) | ||||||||||
| ||||||||||
| ||||||||||
(defn add-vect
[v1 v2]
(make-vect (+ (xcor-vect v1)
(xcor-vect v2))
(+ (ycor-vect v1)
(ycor-vect v2)))) | ||||||||||
| ||||||||||
(defn sub-vect
[v1 v2]
(make-vect (- (xcor-vect v1)
(xcor-vect v2))
(- (ycor-vect v1)
(ycor-vect v2)))) | ||||||||||
| ||||||||||
(defn scale-vect
[c v]
(make-vect (* c (xcor-vect v))
(* c (ycor-vect v)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-47 (:use [sicp.chpt2.ex2-46 :only [make-vect]])) | ||||||||||
Clojure doesn't have a concept of pairs so | ||||||||||
using | ||||||||||
(defn make-frame [origin edge1 edge2] (list origin edge1 edge2)) | ||||||||||
(defn origin-frame [frame] (nth frame 0)) | ||||||||||
(defn edge1-frame [frame] (nth frame 1)) | ||||||||||
(defn edge2-frame [frame] (nth frame 2)) | ||||||||||
| ||||||||||
using records | ||||||||||
(defrecord Frame [origin edge1 edge2]) | ||||||||||
(defn make-frame [origin edge1 edge2] (Frame. origin edge1 edge2)) | ||||||||||
(defn origin-frame [frame] (.origin frame)) | ||||||||||
(defn edge1-frame [frame] (.edge1 frame)) | ||||||||||
(defn edge2-frame [frame] (.edge2 frame)) | ||||||||||
(ns sicp.chpt2.ex2-48 (:use [sicp.chpt2.ex2-46 :only [make-vect]])) | ||||||||||
(defrecord Segment [start end]) | ||||||||||
(defn make-segment [start end] (Segment. start end)) | ||||||||||
(defn start-segment [seg] (.start seg)) | ||||||||||
(defn end-segment [seg] (.end seg)) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-49
(:use [sicp.chpt2.ex2-46 :only [add-vect
scale-vect
make-vect
xcor-vect
ycor-vect]]
[sicp.chpt2.ex2-47 :only [make-frame
origin-frame
edge1-frame
edge2-frame]]
[sicp.chpt2.ex2-48 :only [make-segment
start-segment
end-segment]]
[seesaw.graphics :only [draw line style]])
(:require [seesaw.core :as ss])) | ||||||||||
seesaw is a swing wrapper for Clojure and made the picture language a lot simpler to implement | ||||||||||
There are a few differences from the SICP version | ||||||||||
Applying a | ||||||||||
Since the | ||||||||||
(defn frame-coord-map
[frame]
(fn [v]
(add-vect (origin-frame frame)
(add-vect (scale-vect (xcor-vect v)
(edge1-frame frame))
(scale-vect (ycor-vect v)
(edge2-frame frame)))))) | ||||||||||
(def identity-frame
(make-frame (make-vect 0 0)
(make-vect 1 0)
(make-vect 0 1))) | ||||||||||
Projects the vectors of a frame to match a rectangle of different dimensions | (defn project-frame
[frame x-axis y-axis]
(make-frame (make-vect (* x-axis (xcor-vect (origin-frame frame)))
(* y-axis (ycor-vect (origin-frame frame))))
(scale-vect x-axis (edge1-frame frame))
(scale-vect y-axis (edge2-frame frame)))) | |||||||||
Converts a | (defn segment->line
([segment]
(segment->line identity-frame segment))
([frame segment]
(let [translate (frame-coord-map frame)
start (translate (start-segment segment))
end (translate (end-segment segment))]
(line (xcor-vect start) (ycor-vect start)
(xcor-vect end) (ycor-vect end))))) | |||||||||
(defn segments->painter
[segment-list]
(fn [frame]
(fn [c g]
(let [style (style :foreground :black :stroke 1)
projected (project-frame frame (.getWidth c) (.getHeight c))]
(doseq [segment segment-list]
(draw g (segment->line projected segment) style)))))) | ||||||||||
Initializes a resizable | (defn render
[renderer]
(ss/frame :width 200
:height 200
:visible? true
:content (ss/border-panel
:center (ss/canvas
:background "#ffffff"
:paint renderer)))) | |||||||||
Returns a new | (defn chain
[& renderers]
(fn [c g]
(doseq [r renderers]
(r c g)))) | |||||||||
(def outline-painter
(segments->painter [(make-segment (make-vect 0 0)
(make-vect 0 1))
(make-segment (make-vect 0 1)
(make-vect 1 1))
(make-segment (make-vect 1 1)
(make-vect 1 0))
(make-segment (make-vect 1 0)
(make-vect 0 0))])) | ||||||||||
(def x-painter
(segments->painter [(make-segment (make-vect 0 0)
(make-vect 1 1))
(make-segment (make-vect 0 1)
(make-vect 1 0))])) | ||||||||||
(def diamond-painter
(segments->painter [(make-segment (make-vect 0.5 0)
(make-vect 1 0.5))
(make-segment (make-vect 1 0.5)
(make-vect 0.5 1))
(make-segment (make-vect 0.5 1)
(make-vect 0 0.5))
(make-segment (make-vect 0 0.5)
(make-vect 0.5 0))])) | ||||||||||
(def wave-painter
(segments->painter [;; head
(make-segment (make-vect 0.4 0)
(make-vect 0.3 0.2))
(make-segment (make-vect 0.3 0.2)
(make-vect 0.4 0.4))
(make-segment (make-vect 0.6 0)
(make-vect 0.7 0.2))
(make-segment (make-vect 0.7 0.2)
(make-vect 0.6 0.4))
;; legs
(make-segment (make-vect 0.4 1)
(make-vect 0.5 0.8))
(make-segment (make-vect 0.6 1)
(make-vect 0.5 0.8))
(make-segment (make-vect 0.2 1)
(make-vect 0.3 0.8))
(make-segment (make-vect 0.8 1)
(make-vect 0.7 0.8))
;; left arm
(make-segment (make-vect 0 0.2)
(make-vect 0.2 0.4))
(make-segment (make-vect 0 0.4)
(make-vect 0.2 0.6))
;; right arm
(make-segment (make-vect 1 0.8)
(make-vect 0.8 0.6))
(make-segment (make-vect 1 0.6)
(make-vect 0.8 0.4))
;; body
(make-segment (make-vect 0.2 0.4)
(make-vect 0.4 0.4))
(make-segment (make-vect 0.6 0.4)
(make-vect 0.8 0.4))
(make-segment (make-vect 0.2 0.6)
(make-vect 0.3 0.6))
(make-segment (make-vect 0.8 0.6)
(make-vect 0.7 0.6))
(make-segment (make-vect 0.3 0.6)
(make-vect 0.3 0.8))
(make-segment (make-vect 0.7 0.6)
(make-vect 0.7 0.8))])) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-50
(:use [sicp.chpt2.ex2-46 :only [make-vect
sub-vect]]
[sicp.chpt2.ex2-47 :only [make-frame]]
[sicp.chpt2.ex2-49 :only [frame-coord-map
identity-frame
wave-painter
render]])) | ||||||||||
(defn transform-painter
[painter origin corner1 corner2]
(fn [frame]
(let [m (frame-coord-map frame)
new-origin (m origin)]
(painter
(make-frame new-origin
(sub-vect (m corner1) new-origin)
(sub-vect (m corner2) new-origin)))))) | ||||||||||
(defn flip-vert
[painter]
(transform-painter painter
(make-vect 0 1)
(make-vect 1 1)
(make-vect 0 0))) | ||||||||||
(defn flip-horiz
[painter]
(transform-painter painter
(make-vect 1 0)
(make-vect 0 0)
(make-vect 1 1))) | ||||||||||
(defn rotate90
[painter]
(transform-painter painter
(make-vect 0 1)
(make-vect 0 0)
(make-vect 1 1))) | ||||||||||
(defn rotate180
[painter]
(transform-painter painter
(make-vect 1 1)
(make-vect 0 1)
(make-vect 1 0))) | ||||||||||
(defn rotate270
[painter]
(transform-painter painter
(make-vect 1 0)
(make-vect 1 1)
(make-vect 0 0))) | ||||||||||
alternative implementations | ||||||||||
(def rotate180 (comp rotate90 rotate90)) | ||||||||||
(def rotate270 (comp rotate180 rotate90)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-51
(:use [sicp.chpt2.ex2-46 :only [make-vect]]
[sicp.chpt2.ex2-48 :only [make-segment]]
[sicp.chpt2.ex2-49 :only [render
chain
identity-frame
wave-painter]]
[sicp.chpt2.ex2-50 :only [transform-painter
rotate90
rotate270]])) | ||||||||||
(defn beside
[painter1 painter2]
(let [paint-left (transform-painter painter1
(make-vect 0 0)
(make-vect 0.5 0)
(make-vect 0 1))
paint-right (transform-painter painter2
(make-vect 0.5 0)
(make-vect 1 0)
(make-vect 0.5 1))]
(fn [frame]
(chain (paint-left frame)
(paint-right frame))))) | ||||||||||
(defn below
[painter1 painter2]
(let [paint-top (transform-painter painter1
(make-vect 0 0)
(make-vect 1 0)
(make-vect 0 0.5))
paint-bottom (transform-painter painter2
(make-vect 0 0.5)
(make-vect 1 0.5)
(make-vect 0 1))]
(fn [frame]
(chain (paint-top frame)
(paint-bottom frame))))) | ||||||||||
(defn below
[painter1 painter2]
(rotate270
(beside (rotate90 painter1)
(rotate90 painter2)))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-52
(:use [sicp.chpt2.ex2-45 :only [up-split right-split]]
[sicp.chpt2.ex2-46 :only [make-vect]]
[sicp.chpt2.ex2-48 :only [make-segment]]
[sicp.chpt2.ex2-49 :only [render
chain
identity-frame
segments->painter
wave-painter]]
[sicp.chpt2.ex2-50 :only [flip-vert flip-horiz rotate180]]
[sicp.chpt2.ex2-51 :only [beside below]])) | ||||||||||
(defn dance-painter
[frame]
(let [face-painter (segments->painter [(make-segment (make-vect 0.35 0.3)
(make-vect 0.45 0.3))
(make-segment (make-vect 0.3 0.15)
(make-vect 0.4 0.15))])]
(chain (wave-painter frame)
(face-painter frame)))) | ||||||||||
(defn corner-split
[painter n]
(if (zero? n)
painter
(let [up (up-split painter (dec n))
right (right-split painter (dec n))
corner (corner-split painter (dec n))]
(beside (below painter up)
(below right corner))))) | ||||||||||
(defn square-of-four
[tl tr bl br]
(fn [painter]
(let [top (beside (tl painter)
(tr painter))
bottom (beside (bl painter)
(br painter))]
(below bottom top)))) | ||||||||||
(defn square-limit
[painter n]
(let [combine4 (square-of-four flip-vert rotate180
identity flip-horiz)]
(combine4 (corner-split painter n)))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-53) | ||||||||||
| ||||||||||
(defn memq [s l] (cond (empty? l) false (= s (first l)) l :else (recur s (rest l)))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-54) | ||||||||||
(defn equal?
[l1 l2]
(cond
(and (symbol? l1)
(symbol? l2))
(= l1 l2)
(or (symbol? l1)
(symbol? l2))
false
(and (empty? l1)
(empty? l2))
true
(or (empty? l1)
(empty? l2))
false
:else
(and (equal? (first l1) (first l2))
(recur (rest l1) (rest l2))))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-55) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-56 (:use [sicp.chpt1.ex1-16 :only [fast-expt]])) | ||||||||||
(defn variable? [e] (symbol? e)) | ||||||||||
(defn same-variable? [v1 v2] (= v1 v2)) | ||||||||||
(defn sum?
[e]
(and (coll? e)
(= (first e) '+))) | ||||||||||
(defn addend [e] (nth e 1)) | ||||||||||
(defn augend [e] (nth e 2)) | ||||||||||
(defn =number?
[exp num]
(and (number? exp)
(= exp num))) | ||||||||||
(defn make-sum
[a1 a2]
(cond (=number? a1 0)
a2
(=number? a2 0)
a1
(and (number? a1) (number? a2))
(+ a1 a2)
:else
['+ a1 a2])) | ||||||||||
(defn product?
[e]
(and (coll? e)
(= (first e) '*))) | ||||||||||
(defn multiplier [e] (nth e 1)) | ||||||||||
(defn multiplicand [e] (nth e 2)) | ||||||||||
(defn make-product
[m1 m2]
(cond (or (=number? m1 0)
(=number? m2 0))
0
(=number? m1 1)
m2
(=number? m2 1)
m1
(and (number? m1) (number? m2))
(* m1 m2)
:else
['* m1 m2])) | ||||||||||
(defn exponentiation?
[exp]
(and (coll? exp)
(= '** (first exp)))) | ||||||||||
(defn base [exp] (nth exp 1)) | ||||||||||
(defn exponent [exp] (nth exp 2)) | ||||||||||
(defn make-exponentiation
[b e]
(cond (=number? e 0)
1
(=number? e 1)
b
(and (number? b) (number? e))
(fast-expt b e)
:else
['** b e])) | ||||||||||
(defn 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)))
(exponentiation? exp)
(make-product
(make-product (exponent exp)
(make-exponentiation (base exp)
(make-sum (exponent exp)
-1)))
(deriv (base exp) var))
:else
(throw (Exception. (str "unknown expression type -- DERIV " exp))))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-57 (:require [sicp.chpt2.ex2-56 :as ex2-56])) | ||||||||||
(defn augend
[e]
(if (> (count e) 3)
(conj (drop 2 e) '+)
(nth e 2))) | ||||||||||
(defn multiplicand
[e]
(if (> (count e) 3)
(conj (drop 2 e) '*)
(nth e 2))) | ||||||||||
| ||||||||||
(defn deriv
[exp var]
(with-redefs [ex2-56/augend augend
ex2-56/multiplicand multiplicand]
(ex2-56/deriv exp var))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-58 (:require [sicp.chpt2.ex2-56 :as ex2_56]) (:use [sicp.chpt1.ex1-16 :only [fast-expt]])) | ||||||||||
a sum is a sequence containing a | ||||||||||
the addend/multiplier consists of all terms to the first occurrence of the operator and the augend/multiplicand, the remaining terms | ||||||||||
exponentiation is right-associative unlike addition and multiplication, so the base
consists of all terms to the last occurrence of | ||||||||||
to handle precedence, check for the operator with the lowest precedence first,
| ||||||||||
(defn single?
[e]
(and (coll? e)
(= 1 (count e)))) | ||||||||||
(defn sum?
[e]
(and (coll? e)
(some #{'+} e))) | ||||||||||
(defn addend
[e]
(let [a (take-while (complement #{'+}) e)]
(if (single? a) (first a) a))) | ||||||||||
(defn augend
[e]
(let [a (rest (drop-while (complement #{'+}) e))]
(if (single? a) (first a) a))) | ||||||||||
(defn make-sum
[a1 a2]
(cond (ex2_56/=number? a1 0)
a2
(ex2_56/=number? a2 0)
a1
(and (number? a1) (number? a2))
(+ a1 a2)
:else
[a1 '+ a2])) | ||||||||||
(defn product?
[e]
(and (coll? e)
(some #{'*} e))) | ||||||||||
(defn multiplier
[e]
(let [m (take-while (complement #{'*}) e)]
(if (single? m) (first m) m))) | ||||||||||
(defn multiplicand
[e]
(let [m (rest (drop-while (complement #{'*}) e))]
(if (single? m) (first m) m))) | ||||||||||
(defn make-product
[m1 m2]
(cond (or (ex2_56/=number? m1 0)
(ex2_56/=number? m2 0))
0
(ex2_56/=number? m1 1)
m2
(ex2_56/=number? m2 1)
m1
(and (number? m1) (number? m2))
(* m1 m2)
:else
[m1 '* m2])) | ||||||||||
(defn exponentiation?
[e]
(and (coll? e)
(some #{'**} e))) | ||||||||||
(defn base
[e]
(let [m (rest (drop-while (complement #{'**}) (reverse e)))]
(if (single? m) (first m) (reverse m)))) | ||||||||||
(defn exponent
[e]
(let [m (take-while (complement #{'**}) (reverse e))]
(if (single? m) (first m) (reverse m)))) | ||||||||||
(defn make-exponentiation
[b e]
(cond (ex2_56/=number? e 0)
1
(ex2_56/=number? e 1)
b
(and (number? b) (number? e))
(fast-expt b e)
:else
[b '** e])) | ||||||||||
(defn deriv
[exp var]
(with-redefs [ex2_56/sum? sum?
ex2_56/addend addend
ex2_56/augend augend
ex2_56/make-sum make-sum
ex2_56/product? product?
ex2_56/multiplier multiplier
ex2_56/multiplicand multiplicand
ex2_56/make-product make-product
ex2_56/exponentiation? exponentiation?
ex2_56/base base
ex2_56/exponent exponent
ex2_56/make-exponentiation make-exponentiation]
(ex2_56/deriv exp var))) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-59) | ||||||||||
(defn element-of-set?
[x s]
(cond (empty? s)
false
(= x (first s))
true
:else
(recur x (rest s)))) | ||||||||||
(defn adjoin-set
[x s]
(if (element-of-set? x s)
s
(conj s x))) | ||||||||||
(defn union-set
[s1 s2]
(cond (empty? s1)
s2
(empty? s2)
s1
(element-of-set? (first s1) s2)
(recur (rest s1) s2)
:else
(conj (union-set (rest s1) s2) (first s1)))) | ||||||||||
| ||||||||||
| ||||||||||
alternative implementation | ||||||||||
(defn union-set [s1 s2] (reduce #(adjoin-set %2 %1) s1 s2)) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-60 (:use [sicp.chpt2.ex2-59 :only [element-of-set?]])) | ||||||||||
with either representation, | ||||||||||
| ||||||||||
(defn adjoin-set [x s] (conj s x)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(defn intersection-set
[s1 s2]
(cond (or (empty? s1) (empty? s2))
[]
(element-of-set? (first s1) s2)
(conj (intersection-set (rest s1) s2) (first s1))
:else
(recur (rest s1) s2))) | ||||||||||
| ||||||||||
| ||||||||||
without removing duplicates, a union of 2 sets is simply their concatenation | ||||||||||
(defn union-set [s1 s2] (concat s1 s2)) | ||||||||||
| ||||||||||
| ||||||||||
the alternative implementation of | ||||||||||
(defn union-set [s1 s2] (reduce #(adjoin-set %2 %1) s1 s2)) | ||||||||||
| ||||||||||
| ||||||||||
for applications where a set is required but duplicates are rare and the most common operation is union
this implementation is much more performant, | ||||||||||
(ns sicp.chpt2.ex2-61) | ||||||||||
(defn adjoin-set
[x s]
(cond (empty? s)
(conj s x)
(< x (first s))
(conj s x)
:else
s)) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-62) | ||||||||||
(defn union-set
[s1 s2]
(cond (empty? s1)
s2
(empty? s2)
s1
(< (first s1) (first s2))
(conj (union-set (rest s1) s2) (first s1))
(> (first s1) (first s2))
(conj (union-set s1 (rest s2)) (first s2))
:else
(conj (union-set (rest s1) (rest s2))
(first s1)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-63) | ||||||||||
Both | ||||||||||
Each algorithm walks the tree just once and so both take the same number of steps \(O(n)\). | ||||||||||
(defrecord Tree [entry left right]) | ||||||||||
(defn make-tree [entry left right] (Tree. entry left right)) | ||||||||||
(defn left-branch [tree] (.left tree)) | ||||||||||
(defn right-branch [tree] (.right tree)) | ||||||||||
(defn entry [tree] (.entry tree)) | ||||||||||
(defn tree->list-1
[tree]
(if (nil? tree)
[]
(concat (tree->list-1 (left-branch tree))
[(entry tree)]
(tree->list-1 (right-branch tree))))) | ||||||||||
(defn tree->list-2
[tree]
(letfn [(copy-to-list
[t result-list]
(if (nil? t)
result-list
(recur (left-branch t)
(conj (copy-to-list (right-branch t) result-list)
(entry t)))))]
(copy-to-list tree ()))) | ||||||||||
examples from figure 2.16 | ||||||||||
(def tree-a
(make-tree 7
(make-tree 3
(make-tree 1 nil nil)
(make-tree 5 nil nil))
(make-tree 9
nil
(make-tree 11 nil nil)))) | ||||||||||
(def tree-b
(make-tree 3
(make-tree 1 nil nil)
(make-tree 7
(make-tree 5 nil nil)
(make-tree 9
nil
(make-tree 11 nil nil))))) | ||||||||||
(def tree-c
(make-tree 5
(make-tree 3
(make-tree 1 nil nil)
nil)
(make-tree 9
(make-tree 7 nil nil)
(make-tree 11 nil nil)))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-64 (:use [sicp.chpt2.ex2-63 :only [make-tree]])) | ||||||||||
| ||||||||||
| ||||||||||
At the root node, | ||||||||||
| ||||||||||
(declare partial-tree) | ||||||||||
(defn list->tree [elts] (first (partial-tree elts (count elts)))) | ||||||||||
(defn partial-tree
[elts n]
(if (zero? n)
[nil elts]
(let [left-size (quot (dec n) 2)
[left-tree non-left-elts] (partial-tree elts left-size)
right-size (- n (inc left-size))
this-entry (first non-left-elts)
[right-tree remaining-elts] (partial-tree (rest non-left-elts) right-size)]
[(make-tree this-entry left-tree right-tree) remaining-elts]))) | ||||||||||
| ||||||||||
The naive | ||||||||||
(defn list->tree-naive
[elts]
(when-not (empty? elts)
(let [pivot (quot (count elts) 2)
[left-elts [this-entry & right-elts]] (split-at pivot elts)]
(make-tree this-entry
(list->tree left-elts)
(list->tree right-elts))))) | ||||||||||
(ns sicp.chpt2.ex2-65
(:use [sicp.chpt2.ex2-62 :only [union-set]]
[sicp.chpt2.ex2-63 :only [tree->list-1]]
[sicp.chpt2.ex2-64 :only [list->tree]])) | ||||||||||
Since | ||||||||||
intersection of 2 sorted sequences | ||||||||||
(defn intersection-set
[s1 s2]
(cond (or (empty? s1) (empty? s2))
()
(< (first s1) (first s2))
(recur (rest s1) s2)
(> (first s1) (first s2))
(recur s1 (rest s2))
:else
(conj (intersection-set (rest s1) (rest s2))
(first s1)))) | ||||||||||
| ||||||||||
| ||||||||||
(defn union-btree
[b1 b2]
(list->tree
(union-set (tree->list-1 b1)
(tree->list-1 b2)))) | ||||||||||
| ||||||||||
(defn intersection-btree
[b1 b2]
(list->tree
(intersection-set (tree->list-1 b1)
(tree->list-1 b2)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-66 (:refer-clojure :exclude [key]) (:use [sicp.chpt2.ex2-63 :only [make-tree left-branch right-branch entry]])) | ||||||||||
This is similar to | ||||||||||
(defrecord Record [key data]) | ||||||||||
(defn make-record [key data] (Record. key data)) | ||||||||||
(defn key [record] (.key record)) | ||||||||||
(defn data [record] (.data record)) | ||||||||||
(defn lookup
[given-key set-of-records]
(cond (nil? set-of-records)
nil
(< given-key (key (entry set-of-records)))
(recur given-key (left-branch set-of-records))
(> given-key (key (entry set-of-records)))
(recur given-key (right-branch set-of-records))
:else
(entry set-of-records))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-67) | ||||||||||
Clojure has better alternatives for dynamic dispatch, protocols | ||||||||||
(defprotocol CodeNode (symbols [this]) (weight [this])) | ||||||||||
(defrecord Leaf [sym w]
CodeNode
(symbols [this]
[(.sym this)])
(weight [this]
(.w this))) | ||||||||||
(defn make-leaf [symbol weight] (Leaf. symbol weight)) | ||||||||||
(defn leaf? [this] (= (class this) Leaf)) | ||||||||||
(defrecord CodeTree [left right syms w]
CodeNode
(symbols [this]
(.syms this))
(weight [this]
(.w this))) | ||||||||||
(defn make-code-tree
[left right]
(CodeTree. left
right
(concat (symbols left)
(symbols right))
(+ (weight left)
(weight right)))) | ||||||||||
(defn left-branch [tree] (.left tree)) | ||||||||||
(defn right-branch [tree] (.right tree)) | ||||||||||
Both | ||||||||||
(defn choose-branch
[bit branch]
(cond (zero? bit)
(left-branch branch)
(= 1 bit)
(right-branch branch)
:else
(throw (RuntimeException. (str "Invalid bit, " bit))))) | ||||||||||
(defn decode
[bits tree]
(letfn [(decode-1 [bs current-branch]
(if (empty? bs)
()
(let [next-branch (choose-branch (first bs) current-branch)]
(if (leaf? next-branch)
(conj (decode-1 (rest bs) tree)
(first (symbols next-branch)))
(recur (rest bs) next-branch)))))]
(decode-1 bits tree))) | ||||||||||
example | ||||||||||
(def 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))))) | ||||||||||
(def sample-message [0 1 1 0 0 1 0 1 0 1 1 1 0]) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-68
(:use [sicp.chpt2.ex2-59 :only [element-of-set?]]
[sicp.chpt2.ex2-67 :only [left-branch right-branch leaf? symbols sample-tree]])) | ||||||||||
(defn encode-symbol
[sym tree]
(loop [path [] branch tree]
(cond (leaf? branch)
path
(element-of-set? sym (symbols (left-branch branch)))
(recur (conj path 0) (left-branch branch))
(element-of-set? sym (symbols (right-branch branch)))
(recur (conj path 1) (right-branch branch))
:else
(throw (RuntimeException. (str "Symbol not in tree, " sym)))))) | ||||||||||
(defn encode
[message tree]
(if (empty? message)
[]
(concat (encode-symbol (first message) tree)
(encode (rest message) tree)))) | ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-69
(:use [sicp.chpt2.ex2-67 :only [weight make-code-tree make-leaf decode]]
[sicp.chpt2.ex2-68 :only [encode]])) | ||||||||||
implementing more declarative versions of both | ||||||||||
(defn adjoin-set
[x s]
(let [[smaller larger] (split-with #(< (weight %) (weight x)) s)]
(concat smaller (conj larger x)))) | ||||||||||
(defn make-leaf-set [leaves] (reduce #(adjoin-set %2 %1) () leaves)) | ||||||||||
(defn successive-merge
[leaf-set]
(if (= 1 (count leaf-set))
(first leaf-set)
(recur (adjoin-set (make-code-tree (second leaf-set)
(first leaf-set))
(drop 2 leaf-set))))) | ||||||||||
(defn generate-huffman-tree [leaves] (successive-merge (make-leaf-set leaves))) | ||||||||||
(def tree
(generate-huffman-tree [(make-leaf 'A 8)
(make-leaf 'B 4)
(make-leaf 'D 1)
(make-leaf 'C 1)])) | ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-70
(:use [sicp.chpt2.ex2-67 :only [make-leaf decode]]
[sicp.chpt2.ex2-68 :only [encode]]
[sicp.chpt2.ex2-69 :only [generate-huffman-tree]])) | ||||||||||
(def rock-tree
(generate-huffman-tree [(make-leaf 'A 2)
(make-leaf 'BOOM 1)
(make-leaf 'GET 2)
(make-leaf 'JOB 2)
(make-leaf 'NA 16)
(make-leaf 'SHA 3)
(make-leaf 'YIP 9)
(make-leaf 'WAH 1)])) | ||||||||||
| ||||||||||
With the huffman tree, the encoded message length is 84 bits. With a fixed length code you'd need 3 bits to uniquely represent all 8 words and the total message length would be 108 bits | ||||||||||
(ns sicp.chpt2.ex2-71) | ||||||||||
for \(n=5\), most frequent takes 1 bit, least frequent takes 4 | ||||||||||
| ||||||||||
for \(n=10\), most frequent takes 1 bit, least frequent takes 9 | ||||||||||
| ||||||||||
for such trees in general and for \(n\) symbols, the most frequent symbol requires 1 bit, while the least frequent symbol requires \(n-1\) bits | ||||||||||
(ns sicp.chpt2.ex2-72) | ||||||||||
| ||||||||||
In the trees from ex 2.71, the most frequent symbol requires 1 branch lookup and the least frequent requires \(n-1\) branch lookups where \(n\) is the number of symbols in the tree | ||||||||||
Since the symbol-sets are implemented as lists in this case, branch lookups are \(O(n)\) and encoding a symbol is \(O(n^{2})\) for the least frequent symbol | ||||||||||
(ns sicp.chpt2.ex2-73
(:refer-clojure :exclude [get])
(:use [sicp.chpt2.ex2-56 :only [variable?
same-variable?
make-sum
make-product
make-exponentiation]])) | ||||||||||
| ||||||||||
Clojure is opinionated about mutable state and controls access to it through atoms, refs and agents | ||||||||||
(def dispatch-table (atom {})) | ||||||||||
(defn put [op type item] (swap! dispatch-table assoc-in [op type] item)) | ||||||||||
(defn get [op type] (get-in @dispatch-table [op type] false)) | ||||||||||
(defn operator [exp] (first exp)) | ||||||||||
(defn operands [exp] (rest exp)) | ||||||||||
(defn deriv
[exp var]
(cond (number? exp)
0
(variable? exp)
(if (same-variable? exp var) 1 0)
:else
((get 'deriv (operator exp)) (operands exp) var))) | ||||||||||
(defn addend [ops] (first ops)) | ||||||||||
(defn augend
[ops]
(if (< 2 (count ops))
(conj (rest ops) '+)
(second ops))) | ||||||||||
(defn sum-deriv
[ops var]
(make-sum (deriv (addend ops) var)
(deriv (augend ops) var))) | ||||||||||
(defn multiplier [ops] (first ops)) | ||||||||||
(defn multiplicand
[ops]
(if (< 2 (count ops))
(conj (rest ops) '*)
(second ops))) | ||||||||||
(defn product-deriv
[ops var]
(make-sum
(make-product (multiplier ops)
(deriv (multiplicand ops) var))
(make-product (deriv (multiplier ops) var)
(multiplicand ops)))) | ||||||||||
install sum and product | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(defn base [ops] (first ops)) | ||||||||||
(defn exponent
[ops]
(if (< 2 (count ops))
(conj (rest ops) '**)
(second ops))) | ||||||||||
(defn exponent-deriv
[ops var]
(make-product
(make-product (exponent ops)
(make-exponentiation (base ops)
(make-sum (exponent ops)
-1)))
(deriv (base ops) var))) | ||||||||||
install exponent | ||||||||||
| ||||||||||
| ||||||||||
changing the order of the arguments to | ||||||||||
| ||||||||||
It doesn't matter how the function is stored as long as its accessed the same way | ||||||||||
alternative implementation | ||||||||||
Clojure does have its own take on polymorphic dispatch using multimethods, the same problem can be solved without an explicit dispatch table | ||||||||||
(defmulti deriv-multi (fn [op _ _] op)) | ||||||||||
(defn deriv
[exp var]
(cond (number? exp)
0
(variable? exp)
(if (same-variable? exp var) 1 0)
:else
(deriv-multi (operator exp) (operands exp) var))) | ||||||||||
(defmethod deriv-multi '+ [_ ops var] (sum-deriv ops var)) | ||||||||||
(defmethod deriv-multi '* [_ ops var] (product-deriv ops var)) | ||||||||||
(defmethod deriv-multi '** [_ ops var] (exponent-deriv ops var)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-74 (:refer-clojure :exclude [get]) (:use [sicp.chpt2.ex2-73 :only [get put]])) | ||||||||||
defining | ||||||||||
(defn apply-generic [op & args] (apply (get op (type (first args))) args)) | ||||||||||
Each file would need to store the type information of how its data is structured. For this solution, I'm using clojure records to store both the type information and the data | ||||||||||
| ||||||||||
(defrecord EmployeeList [employees]) | ||||||||||
(defn get-record-list
[employee-list name]
(some (fn [[n rec]]
(when (= n name)
rec))
(.employees employee-list))) | ||||||||||
| ||||||||||
(defrecord EmployeeMap [employees]) | ||||||||||
(defn get-record-map [employee-map name] ((.employees employee-map) name)) | ||||||||||
Installing | ||||||||||
| ||||||||||
(defn get-record [employees name] (apply-generic 'get-record employees name)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(defn get-salary-abbr [employee] (.s employee)) | ||||||||||
(defn get-salary-detailed [employee] (.salary employee)) | ||||||||||
Installing | ||||||||||
| ||||||||||
(defn get-salary [employee] (apply-generic 'get-salary employee)) | ||||||||||
| ||||||||||
| ||||||||||
(defn find-employee-record [files name] (some #(get-record % name) files)) | ||||||||||
| ||||||||||
| ||||||||||
When Insatiable takes over a new company, the company must expose functions which map to Insatiable's employee functions. | ||||||||||
These functions can then be installed just like | ||||||||||
alternative implementation | ||||||||||
Multimethods would work here too, with a dispatch function on type | ||||||||||
When you need to dispatch on type, Clojure provides an alternative, more efficient dispatch mechanism, protocols | ||||||||||
(defprotocol EmployeeFile (get-record [this name])) | ||||||||||
(extend-type EmployeeList
EmployeeFile
(get-record [this name]
(get-record-list this name))) | ||||||||||
(extend-type EmployeeMap
EmployeeFile
(get-record [this name]
(get-record-map this name))) | ||||||||||
| ||||||||||
| ||||||||||
(defprotocol Employee (get-salary [this])) | ||||||||||
(extend-type AbbreviatedEmployee
Employee
(get-salary [this]
(.s this))) | ||||||||||
(extend-type DetailedEmployee
Employee
(get-salary [this]
(.salary this))) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
In this case, all the new company would have to do is extend the | ||||||||||
(ns sicp.chpt2.ex2-75) | ||||||||||
returning a map since clojure maps are functions of their keys | ||||||||||
(defn make-from-mag-ang
[mag ang]
(let [dispatch {'real-part (* mag (Math/cos ang))
'imag-part (* mag (Math/sin ang))
'magnitude mag
'angle ang}]
dispatch)) | ||||||||||
(defn apply-generic [op arg] (arg op)) | ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
| ||||||||||
(ns sicp.chpt2.ex2-76) | ||||||||||
Explicit dispatch requires every user of each implementation to update their code to handle the new type or operation, there is no simple way to determine whether a type or operation is supported | ||||||||||
In the data-directed style, adding a new operation or type requires a new entry in the global lookup table. This can be done with an explicit installation function for the type or the operation. | ||||||||||
With message passing, new operations need to be added to the dispatch function in every type constructor where they're to be supported. A new type, on the other hand, is trivial to add with no change to any existing code or global state. | ||||||||||
In a system where new types are added often, message passing makes the most sense since the impact on the existing system is minimal. If new operations are added more frequently, the data directed approach is preferable. | ||||||||||