sicp-ex-2.1



<< Previous exercise (1.46) | Index | Next exercise (2.2) >>


  
 ;; ex 2.1 
  
 (define (numer x) (car x)) 
  
 (define (denom x) (cdr x)) 
  
 (define (print-rat x) 
   (newline) 
   (display (numer x)) 
   (display "/") 
   (display (denom x))) 
  
 (define (make-rat n d) 
   (let ((g ((if (< d 0) - +) (gcd n d)))) 
     (cons (/ n g) (/ d g)))) 
  
 ;; Testing 
 (print-rat (make-rat 6 9)) ; 2/3 
 (print-rat (make-rat -6 9)) ; -2/3 
 (print-rat (make-rat 6 -9)) ; -2/3 
 (print-rat (make-rat -6 -9)) ; 2/3 
  

There is a bug in the solution above. If gcd is defined as described in 1.2.5, it will have sign depending on the number of iterations it runs and the signs of a and b. For example:

(gcd 1 -2) ; 1
(gcd 6 -9) ; -3

Thus:

(print-rat (make-rat 1 -2)) ; 1/-2
(print-rat (make-rat 6 -9)) ; -2/3

To fix either make gcd return an absolute value or get the absolute value in make-rat (this is implemented below):

(define (make-rat n d)
  (let ((g ((if (< d 0) - +) (abs (gcd n d)))))
    (cons (/ n g) (/ d g))))

category-learning-scheme category-texts


If we don't want to consider the sign of gcd, and still get the right answer, we could implement the following:

(define (make-rat n d)
  (define g (gcd n d))
  (cond ((> (* n d) 0) (cons (abs (/ n g)) (abs (/ d g))))
        ((< (* n d) 0) (cons (- (abs (/ n g))) (abs (/ d g))))))

Lily X. :)


We could save the simplified numbers as variables to make the program more readable too

 (define (make-rat n d) 
         (define g (gcd n d)) 
         (let        ((simple-n (abs (/ n g))) 
                      (simple-d (abs (/ d g)))) 
                 (cond 
                         ((> (* n d) 0) (cons simple-n simple-d)) 
                         ((< (* n d) 0) (cons (- simple-n) simple-d))))) 

Another solution follows:

 (define (make-rat n d) 
   (define (sign x) (if (< x 0) - +)) 
   (let ((g (gcd n d))) 
     (cons ((sign d) (/ n g)) 
               (abs (/ d g))))) 

Evan


Here's a solution that doesn't use conditionals.

 (define (make-rat n d) 
   (let ((g (abs (gcd n d))) 
        (abs-d (abs d))) 
     (cons (/ (* n (/ d abs-d)) g) 
           (/ abs-d g)))) 

Let g take the absolute value of the gcd expression, so its sign won't interfere with the final sign of n and d.

Then, when you give cons, as its first argument, the multiplication of n by (/ d abs-d), you get the following:

Giving abs-d to cons as its second argument removes the possible sign from the denominator.


I really don't see why all the solutions here should look as cluttered as they do.

We just define sign as

 (define (bool->number x) 
         (cond ((equal? x #t) 1) 
               ((equal? x #f) 0) 
               (else "Input must be a boolean!") 
         ) 
 ) 
  
 (define (sign x) 
         (- (bool->number (> x 0)) 
            (bool->number (< x 0)) 
         ) 
 ) 

And then make-rat as

 (define (make-rat n d) 
         (if (= d 0) (error "The denonimator must not be zero!")) 
          
         (define (rat-change x) 
                 (/ (* x (sign d)) (gcd n d)) 
         ) 
          
         (cons (rat-change n) (rat-change d )) 
 )