Objective-C Fractions mixed numbers

Discussion in 'Mac Programming' started by TheHeavySoldier, Mar 8, 2014.

  1. TheHeavySoldier macrumors newbie

    Joined:
    Mar 8, 2014
    #1
    Hey everyone,

    I'm reading "Programming in Objective-C (6th edition)" and one of the exercises is to display the mixed numbers from a fraction, so 5/3 would be
    1 2/3.

    I'm adding 1/2 and 4/2, which results in 10/4, then I have a reduce method which changes 10/4 into 5/2, and this is where I loose track.

    The actual exercise:
    Modify the Fraction’s print method to display fractions greater than 1 as mixed numbers. For example, the fraction 5/3 should be displayed as 1 2/3.

    I made a new method:

    Fraction.h file
    Code:
    #import <Foundation/Foundation.h>
    
    @interface Fraction : NSObject
    
    @property int numerator, denominator;
    
    -(void)printReduced: (bool)b;
    -(void)setNum: (int)n setDi: (int)d;
    -(void)reduce;
    
    [B]-(int)mixedNumbers;[/B]
    
    -(double)convertToNum;
    
    -(Fraction *)add: (Fraction *)f;
    -(Fraction *)subtract: (Fraction*)f;
    -(Fraction *)multiply: (Fraction *)f;
    -(Fraction *)divide: (Fraction *)f;
    
    @end
    
    Fraction.m file
    Code:
    #import "Fraction.h"
    
    @implementation Fraction
    
    @synthesize numerator, denominator; // Name the variables numerator and denominator, instead of _numerator and _denominator, like the @property does automatically
    
    -(void)printReduced: (bool)b {
        if (b == YES) {
            [self reduce];
        }
        if ([self mixedNumbers] > 0) {
            NSLog(@"%i %i/%i", [self mixedNumbers], numerator, denominator);
        } else {
            NSLog(@"%i/%i", numerator, denominator);
    
        }
    }
    
    -(void)setNum: (int)n setDi: (int)d {
        numerator = n;
        denominator = d;
    }
    
    -(void)reduce {
        int u = numerator, v = denominator, temp;
        
        while (v != 0) {
            temp = u % v;
            u = v;
            v = temp;
        }
        
        numerator /= u;
        denominator /= u;
    }
    
    [B]-(int)mixedNumbers {
        int u = numerator, v = denominator, mixNum = 0;
        
        while (u > v) {
            mixNum += 1;
            u -= v;
            numerator -= denominator;
        }
        return mixNum;
    }[/B]
    
    -(double)convertToNum {
        if (denominator != 0) {
            return (double)numerator / denominator;
        } else {
            return NAN;
        }
    }
    
    -(Fraction *)add: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator + denominator * f.numerator;
        result.denominator = denominator * f.denominator;
        
        return result;
    }
    
    -(Fraction *)subtract: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator - denominator * f.numerator;
        result.denominator = denominator * f.denominator;
    
        return result;
    }
    
    -(Fraction *)multiply: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.numerator;
        result.denominator = denominator * f.denominator;
    
        return result;
    }
    
    -(Fraction *)divide: (Fraction *)f {
        Fraction *result = [[Fraction alloc] init];
        
        result.numerator = numerator * f.denominator;
        result.denominator = denominator * f.numerator;
    
        return result;
    }
    
    @end
    
    
    and my main.m file
    Code:
    #import <Foundation/Foundation.h>
    #import "Fraction.h"
    
    int main(int argc, const char * argv[])
    {
    
        @autoreleasepool {
            Fraction *myFraction = [[Fraction alloc] init];
            Fraction *myFractionB = [[Fraction alloc] init];
            
            Fraction *resultFraction;
            
            // Set fraction to 1/3
            [myFraction setNum: 1 setDi: 2];
            [myFractionB setNum: 4 setDi: 2];
            
            // Display the fraction
            NSLog(@"%i/%i + %i/%i is:", myFraction.numerator, myFraction.denominator, myFractionB.numerator,
                                        myFractionB.denominator);
            
            resultFraction = [myFraction add: myFractionB];
            [resultFraction printReduced: YES];
            
        }
        return 0;
    }
    
    
    Ok, so the import part is where it (I think) goes wrong is this:
    Code:
    -(int)mixedNumbers {
        int u = numerator, v = denominator, mixNum = 0;
        
        while (u > v) {
            mixNum += 1;
            u -= v;
            [B]numerator -= denominator;[/B]
        }
        return mixNum;
    }
    
    When numerator -= denominator; is removed, the program will print "2 5/2", when it is left in, the program will print "0 1/2" and I can't figure out why.
    I've tried numerator -= v;, I've taken it out of the while loop and put it right before and after return mixNum;, I've tried a do-while statement, it just won't work.

    If anyone can point out what I've done wrong, I would greatly appreciate it.

    Thanks in advance.
     
  2. mwb macrumors newbie

    Joined:
    Jul 21, 2011
    #2
    The problem is that you call mixedNumbers() twice in your print method

    The first time mixedNumbers is called it'll return 2 and numerator will be 1. The second time it's called u <= v so the while loop doesn't run and it'll return 0. You want the result from the first run.

    After you've fixed your print method to get things working you might want to look up the modulo operation and integer division, then come up with a more elegant solution :)
     
  3. TheHeavySoldier thread starter macrumors newbie

    Joined:
    Mar 8, 2014
    #3
    Thanks for your explanation, that makes sense.
    I've fixed it by creating a local int variable inside the print method:
    Code:
    -(void)printReduced: (bool)b {
        int mixNum = [self mixedNumbers];
        if (b == YES) {
            [self reduce];
        }
        if (mixNum > 0) {
            NSLog(@"%i %i/%i", mixNum, numerator, denominator);
        } else {
            NSLog(@"%i/%i", numerator, denominator);
    
        }
    }
    
    and it displays 2 1/2 now (Yey :))
    I'll see if I can figure out a way to use the modulus and division operator.

    Ok, I figured I would have a better understanding of whats going on if I used a pen and paper to write it down, and I've come up with the following:
    Code:
    @synthesize numerator, denominator, mixNum;
    
    -(void)printReduced: (bool)b {
        if (b == YES) {
            [self reduce];
            [self mixedNumbers];
        }
        if (mixNum > 0) {
            NSLog(@"%i %i/%i", mixNum, numerator, denominator);
        } else {
            NSLog(@"%i/%i", numerator, denominator);
    
        }
    }
    
    .........
    
    -(void)mixedNumbers {
        int u = numerator, v = denominator;
        
        numerator = u % v;
        mixNum = u / v;
    }
    
    I have to say, I like the new method, much cleaner and easier to grasp than my first method.
    Thank you for you help man. :D
     
  4. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #4
    After printReduced is called once, and it prints a mixed number, the just-printed Fraction will stop working correctly in calculations. If it's used, the result will be wrong. This is because your later code prints as a mixed number by altering the value of numerator. The mixNum value then isn't used by any of the calculation methods.

    In general, a print method should only print. It shouldn't break the ongoing usability of the object. After all, you might want to print something more than once, while still using it for calculation.

    You should either change your print method so it doesn't alter numerator, or redo all your calculation methods to use mixNum. Which of those is likely to be easier?
     
  5. TheHeavySoldier thread starter macrumors newbie

    Joined:
    Mar 8, 2014
    #5
    Hey, thanks for the clarification.
    I've thought about what you said and I'm thinking I should use a local variable in the printReduced method, setting the variable to [self mixedNum]
    and returning mixNum and numerator in the mixedNumbers method instead of setting numerator to a new number.
    but I'm not really sure how to do this, mind giving me a hint?
     
  6. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #6
    Try writing the printReduced method without a mixedNumbers method of any kind.

    If you know the numerator and denominator of a fraction, what is the simplest test you can do that tells you whether the rational value is a mixed number or not? That is, what simple distinguishing characteristic does an improper fraction have that a proper fraction does not?
    http://en.wikipedia.org/wiki/Fraction_(mathematics)#Proper_and_improper_fractions

    After you've determined the test, what would you write in printReduced so it prints either a mixed number or a proper fraction?

    After you've determined you should print a mixed number, what's the simplest calculation that will produce the necessary values, without altering the numerator or denominator instance variables? Hint: look at your second mixedNumbers and be sure you understand what each line is doing. Then fill in the blanks in this:
    Code:
    int properNumerator = _____;
    int wholeNumber = _____;
    NSLog(@"%i %i/%i", wholeNumber, properNumerator, denominator);
    

    For added challenge, improve the mixed-number printing code so it prints whole numbers properly. For example, 4/2 should print as "2" rather than "2 0/2".
     
  7. TheHeavySoldier thread starter macrumors newbie

    Joined:
    Mar 8, 2014
    #7
    Hey man, thanks for you help.
    It works properly now.

    Code:
    -(void)printReduced: (bool)b {
        if (b == YES) {
            [self reduce];
        }
        if (numerator >= denominator) {
            int properNumerator = numerator % denominator;
            int wholeNumber = numerator / denominator;
            
            if (properNumerator == 0) {
                printf("%i\n", wholeNumber);
            } else {
                printf("%i %i/%i\n", wholeNumber, properNumerator, denominator);
            }
        } else {
            printf("%i/%i\n", numerator, denominator);
    
        }
    }
    
    I just got rid of the mixedNumbers method entirely.
    Would you have used those if statements yourself as well or would you've done it differently? (I guess my question is, is there a more efficient to handle those if statements)
     
  8. chown33 macrumors 604

    Joined:
    Aug 9, 2009
    #8
    That's pretty much how I would have done it. There are different ways to write it, and what you wrote is fine.

    You have three distinct outcomes, so you'd need at least two if's and an else at absolute minimum. If you want to work on that puzzle, feel free, but I don't think it's worthwhile for something this simple.
     

Share This Page