首页 > 代码库 > [SICP Notes] 1.1 The Elements of Programming
[SICP Notes] 1.1 The Elements of Programming
About the Book
To know more about programming, I have decided to start reading the famous Structure and Interpretation of Computer Programs (SICP, or the Wizard Book), which is first published by MIT in 1985. The Wizard Book used MIT Scheme, which is a dialect of Lisp, as the demonstrating language. This language is so different from the languages I know (C++ and JavaScript, which are both inspired by C). However, the book does not talk much about the characteristics of the language. It focus on teaching important concepts in Computer Science, but not teaching a particular language. I am sure that the book and the interesting language can help me to become a better programmer! (but not just a coder XD) This book is available online under Creative Common (CC) so you may take a look at it too!
Important Quotes from the Book
1.1.3 Evaluating Combinations
- Evaluate the subexpressions of the combination. (recursively!)
- Apply the procedure that is the value of the leftmost subexpression (the operator) to the arguments that are the values of the other subexpressions (the operands).
- the values of numerals are the numbers that they name,
- the values of built-in operators are the machine instruction sequences that carry out the corresponding operations, and
- the values of other names are the objects associated with those names in the environment.
1.1.5 Applicative Order Versus Normal Order
An alternative evaluation model would not evaluate the operands until their values were needed. Instead it would first substitute operand expressions for parameters until it obtained an expression involving only primitive operators, and would then perform the evaluation. This alternative ``fully expand and then reduce‘‘ evaluation method is known asnormal-order evaluation.
1.1.7 Mathematical Functions Versus Computer Procedures
1.1.8 Procedures as Black-Box Abstractions
Local names
Internal definitions and block structure
My Solution to the Exercises
Exercise 1.1
In this exercise, I only need to write down the result of each expression. I have put a semi-colon(;) before the result.
10 ;10 (+ 5 3 4) ;12 (- 9 1) ;8 (/ 6 2) ;3 (+ (* 2 4) (- 4 6)) ;6 (define a 3) (define b (+ a 1)) (+ a b (* a b)) ;19 (= a b) ;#f (if (and (> b a) (< b (* a b))) b a) ;4 (cond ((= a 4) 6) ((= b 4) (+ 6 7 a)) (else 25)) ;16 (+ 2 (if (> b a) b a)) ;6 (* (cond ((> a b) a) ((< a b) b) (else -1)) (+ a 1) ;16
Exercise 1.2
In this exercise, I need to translate a mathematical expression into a complex combination in Scheme.
(/ (+ 5 4 (- 2 (- 3 (+ 6 (/ 4 3))))) (* 3 (- 6 2) (- 2 7)))(well, I have abused the use of indentation in this exercise...)
Exercise 1.3
In this exercise, I need to write a procedure that take three numbers as arguments and returns the sum of the squares of the two larger numbers.
(define (sum-of-squares-of-larger-two a b c) (cond ((and (<= a b) (<= a c)) (+ (* b b) (* c c))) ((and (<= b a) (<= b c)) (+ (* a a) (* c c))) (else (+ (* a a) (* b b)))))
Exercise 1.4
In this exercise, I need to explain the behaviour of the following procedure:
(define (a-plus-abs-b a b) ((if (> b 0) + -) a b))This procedure returns the sum of a and the absolute value of b.
When b is positive, the procedure return a+b.
When b is 0 or negative, the procedure returns a-b (a-b = a-(-|b|) = a+|b|, given b<=0)
Exercise 1.5
Given the following code, is the result different when applicative order and normal order are used?
(define (p) (p)) (define (test x y) (if (= x 0) 0 y)) (test 0 (p))
Applicative Order:
(test 0 (p))
This method would first evaluate all the subexpressions of the combination.
(p) calls (p) again so there is an infinite loop. (runtime_error)
Normal Order:
(test 0 (p))
(if (= 0 0) 0 (p))
0
This method would first substitute operand expressions for parameters until it obtained an expression involving only primitive operators, and would then perform the evaluation. (p) is not evaluated until it is needed, so no infinite loop appears. (0 is returned)
Exercise 1.6
Why the built-in if is a special form instead of a procedure? Given the following definition of a new-if:
(define (new-if predicate then-clause else-clause) (cond (predicate then-clause) (else else-clause)))This new-if looks pretty good when applying to simple cases. However, it is not that good when recursion is used in the clauses. Consider this:
(define (sqrt-iter guess x) (new-if (good-enough? guess x) guess (sqrt-iter (improve guess x) x)))Since the interpreter evaluate combinations in applicative order, it would evaluate all the subexpressions in (new-if predicate then-clause else-clause). As the else-clause of new-if in sqrt-iter is sqrt-iter itself, using new-if would invoke an infinite loop.
If the special form if is used instead, the interpreter would not evaluate both the then-clause and else-clause. The self-calling then clause is only evaluated when predicate returns false.
Exercise 1.7
In the sample (sqrt x) procedure in this chapter, a good-enough procedure is used to judge whether the approximation of the sqrt is accurate enough:
(define (good-enough? guess x) (< (abs (- (square guess) x)) 0.001))We say that the guess is good enough if the difference between guess square and x is fewer than a value, 0.001 here. However, this method has its limitation. When x is very big or very small, the fixed value of 0.001 cannot play its role effectively. (you can try!) So I write a new procedure for this exercise:
(define (average x y) (/ (+ x y) 2.0)) (define (square x) (* x x)) (define (sqrt x) (define (good-enough? guess old-guess) (< (abs (/ (- guess old-guess) old-guess)) 0.00000001)) (define (improve guess) (average guess (/ x guess))) (define (sqrt-iter guess old-guess) (if (good-enough? guess old-guess) guess (sqrt-iter (improve guess) guess))) (sqrt-iter 1.0 -1.0))Notice that I have put procedures like good-enough? and improve into the definition of sqrt. Doing so can reduce confusion (as good-enough? and guess are common names but should only be useful in the sqrt procedure) and reduce the number of parameters in each procedure (x needs not to be passed due to its lexical scope).
Exercise 1.8
This one is similar to 1.7 but this time I am solving for the cube root.
(define (pow x y) (if (= y 0) 1 (* x (pow x (- y 1))))) (define (cube-root x) (define (good-enough? guess old-guess) (< (abs (/ (- guess old-guess) old-guess)) 0.00000001)) (define (improve guess) (/ (+ (/ x (pow guess 2)) (* 2 guess)) 3)) (define (cube-root-iter guess old-guess) (if (good-enough? guess old-guess) guess (cube-root-iter (improve guess) guess))) (cube-root-iter 1.0 -1.0)) (cube-root -8)I have added a test case -8 to show that this procedure can handle the negative root as well.
Do you find coding in Scheme interesting? Or are you interested in the Wizard Book? Join me to read the book so that we can share our thoughts!