functionschemesicp

Are Scheme functions one liners?


I am following Structure and Interpretation of Computer Programs and while attempting to solve Ex 1.3 I arrived at the following code as my first attempt:

(define (sumsq a b c)(
(define highest (if (> (if (> a b) a b) (if (> a c) a c)) (if (> a b) a b) (if (> a c) a c)))
(define second_h (if (> (if (> a b) b a) (if (> a c) c a)) (if (> a b) b a) (if (> a c) c a)))
(+ (* highest highest) (* second_h second_h)))

It was not working and I looked up the solution and found them at SICP Wiki

;; ex 1.3
;; implemented using only techniques covered to this point

(define (square x) (* x x))

(define (sum-of-squares x y)
  (+ (square x) (square y)))

(define (largest-two-of-three x y z)
  (if (>= x y)
      (sum-of-squares x (if (>= y z) y z))
      (sum-of-squares y (if (>= x z) x z))))

The difference was that I was using multiple statements to define variables and then sum the squares, while the correct way is to define each of my lines as functions.

Are functions in Scheme one liners? Or did I miss the whole thing?


Solution

  • What you wrote (minus one extra paren) is:

    (define (sumsq a b c)
      (define highest
        (if (> (if (> a b) a b)
               (if (> a c) a c))
          (if (> a b) a b)
          (if (> a c) a c)))
      (define second_h
        (if (> (if (> a b) b a)
               (if (> a c) c a))
          (if (> a b) b a)
          (if (> a c) c a)))
      (+ (* highest highest) (* second_h second_h)))
    

    Their solution does separate squares and sum-of-squares into separate functions, but I don't think that that's what's important. Not writing (+ (* a a) (* b b)) will stop you from having to name the two values you're calculating, which will let you write the function as one big expression at the end, but there are bigger things to worry about now.

    I think the problem you're having is that your (if...) expressions are too big to easily understand. Notice that there are two patterns which show up many times: (if (> a b) a b) and (if (> a b) b a). These are the max and min functions, so it's useful to define them as such:

    (define (min a b) (if (< a b) a b))
    (define (max a b) (if (< a b) b a))
    

    This way, you can rewrite your solution as:

    (define (sumsq a b c)
      (define highest
        (if (> (max a b) (max a c))
          (max a b)
          (max a c)))
      (define second_h
        (if (> (min a b) (min a c))
          (min a b)
          (min a c)))
      (+ (* highest highest) (* second_h second_h)))
    

    Simplifying it again gives:

    (define (sumsq a b c)
      (define highest
        (max (max a b) (max a c)))
      (define second_h
        (max (min a b) (min a c)))
      (+ (* highest highest) (* second_h second_h)))
    

    Notice how this writing is much more easy to reason with, (max (max a b) (max a c)) is obviously the maximum of a b and c, and can actually be rewritten as (max (max a b) c). Looking at second_h, though, it's not obvious that it's correct. What will happen when a is the smallest of the three values?

    The trick they use in their solution is to first compare x and y. if x < y, then you know that y is not the smallest of the three, so it is either highest or second highest. The other number you will want to use is the higher of x and z, since the lower of those two will be the smallest of the three, which you want to ignore. Similar logic applies when y < x.