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. Here's the original
make-rat
:
(define (make-rat n d) (let ((g (gcd n d))) (cons (/ n g) (/ d g))))Before we start into changing this, first let's define what we mean by a positive versus a negative rational number. It's all in the signs... A rational number is positive if the numerator and denominator have the same sign and negative if the numerator and denominator have different signs.
So if the arguments to
make-rat
are either both positive or both negative then we want make-rat
to convert both the numerator and denominator to their absolute values. If, on the other hand, the arguments have different signs then we want make-rat
to ensure that the numerator has a negative sign and converts the denominator to its absolute value.Looking at the denominator first, we always want that to be converted to its absolute value. This can be easily achieved by applying
abs
to the passed denominator argument, d
.As for the numerator, we know we can change the sign of a number by multiplying it by -1 and that multiplying a number by 1 leaves the sign unchanged. Let's look at what we'd have to multiply the numerator by in the various cases to get the effect we want:
Numerator Sign | Denominator Sign | Desired Numerator Sign | Multiplicand |
---|---|---|---|
+ve | +ve | +ve | 1 |
-ve | -ve | +ve | -1 |
-ve | +ve | -ve | 1 |
+ve | -ve | -ve | -1 |
As you can see, the multiplicand we want to use corresponds to the sign of the denominator. Handily there's a standard mathematical function, the sign, or signum, function, that will return 1, 0 or -1 depending upon whether the argument is positive, zero or negative. So we can apply this to the denominator and multiply the numerator by the result.
Unfortunately signum is not a standard scheme function. DrRacket, the interpreter I'm using, has a predefined procedure, sng, that provides this functionality. If you're using something else then you can define it as:
(define (sgn x) (if (= x 0) 0 (/ (abs x) x)))Assuming we've got
sgn
to hand we can then define our updated make-rat
as:
(define (make-rat n d) (let ((g (gcd n d))) (cons (/ (* n (sgn d)) g) (/ (abs d) g))))And then, assuming we've also got
print-rat
and the supporting procedures from section 2.1.1, we can put it into practice:
> (print-rat (make-rat 4 5)) 4/5 > (print-rat (make-rat 4 -5)) -4/5 > (print-rat (make-rat -4 5)) -4/5 > (print-rat (make-rat -4 -5)) 4/5
No comments:
Post a Comment