Exercise 1.2

SICP Exercise 1.2.
Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.
This took me back to the early days of learning to code. It’s humbling to go back to fundamentals, and have to work through logic problems that don’t come easily to my brain.
The first thing I started doing was: I tried to find a way to capture the two larger numbers, but I quickly realized I was running into two problems. 1) I didn’t yet have a way (in Lisp) to return a list of values to be used later, and 2) I didn’t yet have a way to order numbers (because if I could order them I could sum the square of the last two, for example.)
The one thing I did know was that if I was going to find the sum of the squares of the two larger numbers, I needed the sum-of-squares function. And in order to run the sum-of-squares function, I needed the square function defined. Both of these were defined for me already in the book. So I should define those first.
1(define (square x)
2 (* x x))
3
4(define (sum-of-squares x y)
5 (+ (square x) (square y)))
Next I needed to define a function (let’s call it two-greatest-numbers - because sum-of-square-of-two-greatest-numbers is a tad too long) that takes three arguments: x, y, and z.
Of all the case analyses they covered so far in the book, cond made the most sense, since if is a “restricted type of conditional that can be used when there are precisely two cases in the case analysis.”
So I wanted a conditional expression that could compare the three numbers to each other, and feed those into the sum-of-squares function.
The first thing I tried to do was figure out how many possible variations you could have with three numbers:
- All three are different
- All three are the same
- Two are the same and one is higher
- Two are the same and one is lower
It didn’t take long to realize, though, that doing this would create an enormous amount of predicates to evaluate.
- If all three are equal, find the sum of the squares of any two.
- If all three are different, compare x with y, and y with z. Then compare:
- If x is greater than y, and y is greater than z, find the sum of the squares of x and y.
- If y is greater than x, and x is greater than z, find the sum of the squares of y and x.
- If y is greater than x, and x is less than z, find the sum of the squares of y and z.
- If z is greater than x, and x is less than y, find the sum of the squares of z and y.
- If x is greater than y, and y is less than z, find the sum of the squares of x and z.
- If z is greater than x, and x is greater than y, find the sum of the squares of z and x.
So, in total, there are really only three variations, because you end up only performing the sum-of-squares function on either x and y, x and z, or y and z.
If that’s the case, there has to be a different way to figure this out.
Because this gives me a headache to look at, and honestly, I lost track of whether I had covered all my cases. I also realized that it didn’t make sense to do separate cases for = and >, when they gave an example in the book of how to define >= and <=.
1(define (two-greatest-numbers x y z)
2 (cond ((and (= x y) (= y z)) (sum-of-squares x y))
3 ((and (= x y) (> z x)) (sum-of-squares x z))
4 ((and (= x z) (> y x)) (sum-of-squares x y))
5 ((and (= y z) (> x y)) (sum-of-squares x y))
6 ((and (= x y) (< z x)) (sum-of-squares x y))
7 ((and (= x z) (< y x)) (sum-of-squares x z))
8 ((and (= y z) (< x y)) (sum-of-squares y z))
9 ((and (> x y) (> y z)) (sum-of-squares x y))
10 ((and (> y x) (> x z)) (sum-of-squares x y))
11 ((and (> y x) (< x z)) (sum-of-squares y z))
12 ((and (> z x) (< x y)) (sum-of-squares y z))
13 ((and (> x y) (< y z)) (sum-of-squares x z))
14 ((and (> z x) (> x y)) (sum-of-squares z x))))
So instead of finding the two greatest numbers, I had to think about it in reverse: if there are three numbers and two are larger, that means one is the smallest. So can I find the smallest number, then sum the square of the other two?
Conditionally that would be:
- If x is less than or equal to y and x is less than or equal to z, then x is the smallest. Return the sum of the squares of y and z.
- If y is less than or equal to x and y is less than or equal to z, then y is the smallest. Return the sum of the squares of x and z.
- Otherwise, z is the smallest. Return the sum of the squares of x and y.
All together, this would look like:
1(define (square x)
2 (* x x))
3
4(define (sum-of-squares x y)
5 (+ (square x) (square y)))
6
7(define (<= x y)
8 (not (> x y)))
9
10(define (two-larger-numbers x y z)
11 (cond ((and (<= x y) (<= x z)) (sum-of-squares y z)) ;; x is the smallest
12 ((and (<= y x) (<= y z)) (sum-of-squares x z)) ;; y is the smallest
13 (else (sum-of-squares x y)) ;; z is the smallest
14 )
15)