Permalink
Browse files

finished section 1.1

  • Loading branch information...
1 parent 29a3e69 commit b81a35e0c13853604676b425179f1a0e8ba4bd66 Robert Campbell committed Nov 29, 2011
Showing with 139 additions and 62 deletions.
  1. +139 −62 src/sicp/chapter1.clj
View
@@ -1,55 +1,65 @@
-(ns sicp.chapter1)
+(ns sicp.chapter1
+ (:use clojure.test))
-;;;; Chapter 1
+;;;; Chapter 1 - Building Abstractions with Procedures
+;;;; http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-9.html#%_chap_1
+
+;;;; Section 1.1 - The Elements of Programming
+;;;; http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-10.html#%_sec_1.1
;;; Exercise 1.1
-(assert (= 10 10))
-(assert (= 12 (+ 5 3 4)))
-(assert (= 8 (- 9 1)))
-(assert (= 3 (/ 6 2)))
-(assert (= 6 (+ (* 2 4) (- 4 6))))
-(assert (= (def a 3) (var a))) ; Scheme returns nil, while
- ; Clojure returns the newly
- ; created var. Also, since
- ; evaluation is from left to
- ; right, the def must be to the
- ; left of the literal var.
-(assert (= (def b (+ a 1)) (var b)))
-(assert (= 19 (+ a b (* a b))))
-(assert (= false (= a b))) ; Clojure uses true/false rather than
+
+(is (= 10 10))
+(is (= 12 (+ 5 3 4)))
+(is (= 8 (- 9 1)))
+(is (= 3 (/ 6 2)))
+(is (= 6 (+ (* 2 4) (- 4 6))))
+(is (= (def a 3) (var a))) ; Scheme returns nil, while
+ ; Clojure returns the newly
+ ; created var. Also, since
+ ; evaluation is from left to
+ ; right, the def must be to the
+ ; left of the literal var.
+(is (= (def b (+ a 1)) (var b)))
+(is (= 19 (+ a b (* a b))))
+(is (= false (= a b))) ; Clojure uses true/false rather than
; Scheme's #t/#f
-(assert (= 4 (if (and (> b a) (< b (* a b)))
- b
- a)))
-(assert (= 16 (cond (= a 4) 6 ; Clojure's cond doesn't require the
- ; predicate+consequent expression
- ; clauses to be wrapped in parens
- ; like Scheme does.
- (= b 4) (+ 6 7 a)
- :else 25))) ; Clojure uses an :else keyword
- ; rather than the else symbol
- ; found in Scheme
-(assert (= 6 (+ 2 (if (> b a) b a))))
-(assert (= 16 (* (cond (> a b) a
- (< a b) b
- :else -1)
- (+ a 1))))
+(is (= 4 (if (and (> b a) (< b (* a b)))
+ b
+ a)))
+(is (= 16 (cond (= a 4) 6 ; Clojure's cond doesn't require the
+ ; predicate+consequent expression
+ ; clauses to be wrapped in parens
+ ; like Scheme does.
+ (= b 4) (+ 6 7 a)
+ :else 25))) ; Clojure uses an :else keyword
+ ; rather than the else symbol
+ ; found in Scheme
+(is (= 6 (+ 2 (if (> b a) b a))))
+(is (= 16 (* (cond (> a b) a
+ (< a b) b
+ :else -1)
+ (+ a 1))))
+
;;; Exercise 1.2
-(assert (= -37/150
- (/ (+ 5
- 4
- (- 2
- (- 3
- (+ 6 4/5))))
- (* 3
- (- 6 2)
- (- 2 7)))))
+
+(is (= -37/150
+ (/ (+ 5
+ 4
+ (- 2
+ (- 3
+ (+ 6 4/5))))
+ (* 3
+ (- 6 2)
+ (- 2 7)))))
+
;;; Exercise 1.3
-(defn square [x] (* x x))
-(defn sum-of-squares [x y]
+(defn- square [x] (* x x))
+
+(defn- sum-of-squares [x y]
(+ (square x)
(square y)))
@@ -62,62 +72,129 @@
(>= c a) (cond (>= a b) (sum-of-squares c a)
:else (sum-of-squares c b))))
-;; Mine is overly complex. Nuno's is better:
+;; Mine is overly complex. Nuno's version is better:
;; https://github.com/nfma/sicp/blob/master/section1.1/ex1.3.txt
(defn largest-sum-of-squares' [a b c]
"Using higher-order fns"
(let [larger ((comp (partial take 2) reverse sort) [a b c])]
(apply + (map * larger larger))))
+
;;; Exercise 1.4
;;; The if conditional selects which operator to use at runtime based
;;; on the value of b.
+
;;; Exercise 1.5
;;; If Ben uses an applicative-order interpreter the expression
;;; (test 0 (p)) will evaluate forever, recursively evaluating the
;;; operand (p) to no end. If Ben is uses a normal-order interpreter,
;;; the evaluation will return 0 since (p) will never have to be
;;; evaluated due to the short-circuited if conditional.
+
;;; Excercise 1.6
;;; Using new-if the sqrt-iter procedure would recurse forever because
;;; new-if is not a special form with special operand evaluation
;;; rules. In other words, new-if will eagerly evaluate its operands -
;;; in this case a recursive call to sqrt-iter - rather than using
;;; lazy evaluation like the primative if special form.
+
;;; Excercise 1.7
-(letfn [(average [x y] (/ (+ x y) 2))
- (improve [guess x] (average guess (/ x guess)))
- (good-enough? [guess x] (< (Math/abs (- (square guess) x))
- 0.001))]
- (defn sqrt
- ([x]
- (sqrt 1.0 x))
- ([guess x]
- (if (good-enough? guess x)
- guess
- (recur (improve guess x)
- x)))))
-(square (sqrt 0.0001)) ; 0.001... fail
+(def tolerance 0.001)
+
+(defn- within-tolerance? [x y]
+ (< (Math/abs (- x
+ y))
+ tolerance))
+
+(defn- average [x y]
+ (/ (+ x y) 2))
+
+(defn- ^:dynamic improve [guess x]
+ (average guess (/ x guess)))
+
+(defn- ^:dynamic good-enough? [guess x]
+ (within-tolerance? (square guess) x))
+
+(defn ^:dynamic sqrt
+ ([x]
+ (sqrt 1.0 x))
+ ([guess x]
+ (if (good-enough? guess x)
+ guess
+ (recur (improve guess x)
+ x))))
+
+(is (within-tolerance? (sqrt Integer/MAX_VALUE)
+ (Math/sqrt Integer/MAX_VALUE)))
+
+;; Show how it's broken for smaller values...
+(let [small-num 0.0001]
+ (is (not (within-tolerance? (sqrt small-num)
+ (Math/sqrt small-num))))) ; sqrt completes,
+ ; but the result
+ ; is outside our
+ ; tolerance
+
+;; Show how it's broken for larger values...
+(binding [improve (fn [old-guess x]
+ (let [new-guess (average old-guess (/ x old-guess))]
+ (if (< (Math/abs (- x new-guess))
+ (Math/abs (- x old-guess)))
+ new-guess
+ (throw (RuntimeException.
+ "New guess is worse!")))))]
+ (is (thrown-with-msg? RuntimeException #"New guess is worse!"
+ (sqrt Long/MAX_VALUE))))
+
+;; Now fix it
+(binding [good-enough? (fn [old-guess new-guess]
+ (< (/ (Math/abs (- old-guess
+ new-guess))
+ old-guess)
+ 0.00001)) ; very small percentage change
+ sqrt (fn ([x]
+ (sqrt 0.1 1.0 x))
+ ([old-guess new-guess x]
+ (if (good-enough? old-guess new-guess)
+ new-guess
+ (recur new-guess
+ (improve new-guess x)
+ x))))]
+ (let [small-num 0.0001]
+ (is (within-tolerance? (sqrt small-num)
+ (Math/sqrt small-num))))
+ (let [large-num Long/MAX_VALUE]
+ (is (within-tolerance? (sqrt large-num)
+ (Math/sqrt large-num)))))
-;; TODO, fix good-enough per execise
;;; Excercise 1.8
+
(letfn [(cube [x] (* x x x))
(improve [guess x] (/ (+ (/ x
(square guess))
(* 2 guess))
3))
- (good-enough? [guess x] (< (Math/abs (- (cube guess) x))
- 0.001))]
- (defn cube-root
+ (good-enough? [guess x] (within-tolerance? (cube guess) x))]
+
+ (defn cbrt
([x]
(cube-root 1.0 x))
([guess x]
(if (good-enough? guess x)
guess
(recur (improve guess x)
- x)))))
+ x))))
+
+ (is (within-tolerance? (cbrt Integer/MAX_VALUE)
+ (Math/cbrt Integer/MAX_VALUE))))
+
+
+;;;; Section 1.2 - Procedures and the Processes They Generate
+;;;; http://mitpress.mit.edu/sicp/full-text/book/book-Z-H-11.html#%_sec_1.2
+
+

0 comments on commit b81a35e

Please sign in to comment.