2
\$\begingroup\$

Given the following task from SICP

Exercise 2.1

Define a better version of make-rat that handles both positive and negative arguments. Make-rat should normalize the sign so that if the rational number is positive, both the numerator and denominator are positive, and if the rational number is negative, only the numerator is negative.

And these prerequisite functions:

(define (gcd a b) (if (= b 0) a (gcd b (remainder a b))))

(define (numer a) (car a))
(define (denom a) (cdr a))
(define (print-rat a)
  (newline)
  (display (numer a))
  (display "/")
  (display (denom a)))

(define (add-rat a b)
  (make-rat (+ (* (numer a) (denom b))
               (* (numer b) (denom a)))   
            (* (denom a) (denom b))))
(define (sub-rat a b)
  (make-rat (- (* (numer a) (denom b))
               (* (numer b) (denom a)))

            (* (denom a) (denom b))))
(define (mul-rat a b)
  (make-rat (* (numer a) (numer b))
            (* (denom a) (denom b))))
(define (div-rat a b)
  (make-rat (* (numer a) (denom b))
            (* (numer b) (denom a))))

I wrote this version of (make-rat ... ). What do you think?

(define (make-rat n d)
  (define (sign n d) (if (> (* n d) 0) + -))
  (let* ((abs-n (abs n)) 
         (abs-d (abs d)) 
         (div (gcd abs-n abs-d)))
    (cons ((sign n d) (/ abs-n div)) 
          (/ abs-d div))))
\$\endgroup\$
2
\$\begingroup\$

To normalize a fraction, one needs to do two things:

  1. fix the signs of numerator and denominator; and
  2. reduce them to their lowest terms.

Your implementation does these two things just fine. However, there is an easier way. To do (1), simply negate both numerator and denominator if the denominator is negative. To do (2), divide by the greatest common divisor.

One could combine the two steps above. Divide the numerator and denominator by the greatest common divisor with its sign fixed to be the same as the denominator's sign. Implementation follows:

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

If gcd always returns a positive number (which your implementation does not), you may remove the call to abs above.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.