<< 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

Comment on how the above solution works:

n d g n/g d/g rat + + + + + + - + + - + - + - - - + - - - - + + +

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:

- Numerator becomes negative if it was originally positive AND D is negative;

- Numerator remains negative if it was originally negative AND D is positive;

- Numerator remains positive if it was originally positive AND D is also positive;

- Numerator becomes positive if it was originally negative AND D is also negative;

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 ))
)
```

A little redundant but I think very readable:

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

Entropy Donary