tags:

views:

711

answers:

10

I'm trying to learn scheme via SICP. Exercise 1.3 reads as follow: Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers. Please comment on how I can improve my solution.

(define (big x y)
    (if (> x y) x y))

(define (p a b c)
    (cond ((> a b) (+ (square a) (square (big b c))))
          (else (+ (square b) (square (big a c))))))
+3  A: 

Looks ok to me, is there anything specific you want to improve on?

You could do something like:

(define (max2 . l)
  (lambda ()
    (let ((a (apply max l)))
      (values a (apply max (remv a l))))))

(define (q a b c)
  (call-with-values (max2 a b c)
    (lambda (a b)
      (+ (* a a) (* b b)))))

(define (skip-min . l)
  (lambda ()
    (apply values (remv (apply min l) l))))

(define (p a b c)
  (call-with-values (skip-min a b c)
    (lambda (a b)
      (+ (* a a) (* b b)))))

And this (proc p) can be easily converted to handle any number of arguments.

leppie
I believe the OP was asking for comments on style, and in that department there are some things that could be talked about, such as what's in my post. :-)
Chris Jester-Young
P.S. I see you on #scheme (though you seem to keep getting logged out)! Small world!
Chris Jester-Young
Yeah, seems my ISP is heavily shaping traffic on some ports. Funny thing is, I can send data, it just takes ages to receive anything :( That said, I didnt know the world had more than 100 Scheme users! hehe
leppie
Bummer about your ISP. Yes, it's nice to know there's a good handful of Schemers out here on the Internet.... :-)
Chris Jester-Young
I'm a total newbie to scheme, so it's great to see your alternative solution (which is way over my head).
ashitaka
+10  A: 

big is called max. Use standard library functionality when it's there.

My approach is different. Rather than lots of tests, I simply add the squares of all three, then subtract the square of the smallest one.

(define (exercise1.3 a b c)
  (let ((smallest (min a b c))
        (square (lambda (x) (* x x))))
    (+ (square a) (square b) (square c) (- (square smallest)))))

Whether you prefer this approach, or a bunch of if tests, is up to you, of course.


Alternative implementation using SRFI 95:

(define (exercise1.3 . args)
  (let ((sorted (sort! args >))
        (square (lambda (x) (* x x))))
    (+ (square (car sorted)) (square (cadr sorted)))))

As above, but as a one-liner (thanks synx @ freenode #scheme); also requires SRFI 1 and SRFI 26:

(define (exercise1.3 . args)
  (apply + (map! (cut expt <> 2) (take! (sort! args >) 2))))
Chris Jester-Young
I think doing square is more expensive than a few extra tests. But that's just me :)
leppie
I think code should optimise clarity first, performance second. However, I'm willing to accept that reasonable people can disagree on this. :-)
Chris Jester-Young
I believe code should do only as intended :)
leppie
This is a very interesting solution. There's more than one way to skin the cat.
ashitaka
min, let, lambda, sort!, map!, cut car, cadr, take!, and apply are not yet introduced at that point in the text.
Shawn J. Goff
@Shawn: I didn't intend my solutions to be ones that people would implement on first reading, but rather what they might like to implement given a certain amount of Scheme experience. Besides, adventurous first readers might use such a solution as a basis for learning about parts of Scheme (and SRFIs) that aren't covered in the book.
Chris Jester-Young
+2  A: 

You can also sort the list and add the squares of the first and second element of the sorted list:

(require (lib "list.ss")) ;; I use PLT Scheme

(define (exercise-1-3 a b c)
  (let* [(sorted-list (sort (list a b c) >))
         (x (first sorted-list))
         (y (second sorted-list))]
    (+ (* x x) (* y y))))
Sébastien RoccaSerra
Sébastien, I haven't learnt yet how to work with lists; but again an interesting solution. I am using PLT-scheme. Will that require work on PLT-scheme ?
ashitaka
AFAIK, that _only_ works on PLT Scheme. :-)
Chris Jester-Young
Yes, I'm a DrScheme user
Sébastien RoccaSerra
+3  A: 

What about something like this?

(define (p a b c)
  (if (> a b)
      (if (> b c)
          (+ (square a) (square b))
          (+ (square a) (square c)))
      (if (> a c)
          (+ (square a) (square b))
          (+ (square b) (square c)))))
Scott Hoffman
Voted up cause I was struggling on this and my code was on similar lines. I'm only learning and don't know yet the bigger constructs in the other examples. With your code I corrected my faulty one. Since I'm not able to add html code in here, I'm putting in my code in a separate reply below.
Christy John
A: 

Here's yet another way to do it:

#!/usr/bin/env mzscheme
#lang scheme/load

(module ex-1.3 scheme/base
  (define (ex-1.3 a b c)
    (let* ((square (lambda (x) (* x x)))
           (p (lambda (a b c) (+ (square a) (square (if (> b c) b c))))))
      (if (> a b) (p a b c) (p b a c))))

  (require scheme/contract)
  (provide/contract [ex-1.3 (-> number? number? number? number?)]))

;; tests
(module ex-1.3/test scheme/base
  (require (planet "test.ss" ("schematics" "schemeunit.plt" 2))
           (planet "text-ui.ss" ("schematics" "schemeunit.plt" 2)))
  (require 'ex-1.3)

  (test/text-ui
   (test-suite
    "ex-1.3"
    (test-equal? "1 2 3" (ex-1.3 1 2 3) 13)
    (test-equal? "2 1 3" (ex-1.3 2 1 3) 13)
    (test-equal? "2 1. 3.5" (ex-1.3 2 1. 3.5) 16.25)
    (test-equal? "-2 -10. 3.5" (ex-1.3 -2 -10. 3.5) 16.25)
    (test-exn "2+1i 0 0" exn:fail:contract? (lambda () (ex-1.3 2+1i 0 0)))
    (test-equal? "all equal" (ex-1.3 3 3 3) 18))))

(require 'ex-1.3/test)

Example:

$ mzscheme ex-1.3.ss
6 success(es) 0 failure(s) 0 error(s) 6 test(s) run
0
J.F. Sebastian
+1  A: 

Using only the concepts introduced up to that point of the text, which I think is rather important, here is a different solution:

(define (smallest-of-three a b c)
        (if (< a b)
            (if (< a c) a c)
            (if (< b c) b c)))

(define (square a)
        (* a a))

(define (sum-of-squares-largest a b c) 
        (+ (square a)
           (square b)
           (square c)
           (- (square (smallest-of-three a b c)))))
Shawn J. Goff
+4  A: 

Using only the concepts presented at that point of the book, I would do it:

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

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

(define (min x y) (if (< x y) x y))

(define (max x y) (if (> x y) x y))

(define (sum-squares-2-biggest x y z)
  (sum-of-squares (max x y) (max z (min x y))))
Carlos Santos
+3  A: 

I did it with the following code, which uses the built-in min, max, and square procedures. They're simple enough to implement using only what's been introduced in the text up to that point.

(define (sum-of-highest-squares x y z)
   (+ (square (max x y))
      (square (max (min x y) z))))
Bill the Lizard
+1  A: 

With Scott Hoffman's and some irc help I corrected my faulty code, here it is

(define (p a b c)
    (cond ((> a b)
     (cond ((> b c)
      (+ (square a) (square b)))
      (else (+ (square a) (square c)))))
     (else
      (cond ((> a c)
       (+ (square b) (square a)))
       (else (+ (square b) (square c))))))
Christy John
A: 

I've had a go:

(define (procedure a b c)
    (let ((y (sort (list a b c) >)) (square (lambda (x) (* x x))))
        (+ (square (first y)) (square(second y)))))
Eric