Objective-C Programming help.

Discussion in 'Mac Programming' started by iMasterWeb, Aug 31, 2009.

  1. iMasterWeb macrumors regular

    Joined:
    Mar 15, 2009
    #1
    Hi there! So I'm learning Objective-C with the book: Programming in Objective-C 2.0 by Stephen Kochan. One of the programs use in the book is a program for adding fractions. Well, I'm the kind of guy that likes to take things to the next level, so I edited the program so that it also subtracts, and makes all improper fraction mixed numbers. One of the problems I'm having is that when doing subtraction I always get the same answer. Here's my code. (I'll add comments so you can keep track of what's going on)

    The @interface file:
    Code:
    //you shouldn't need much explanation here
    #import <Foundation/Foundation.h>
    
    @interface Fraction: NSObject {
    	int numerator;
    	int denominator;
    }
    
    @property int numerator, denominator;
    -(void)    print;
    -(void)    setTo: (int) n over: (int) d;
    -(double)  convertToNum;
    -(void)    add: (Fraction *) f;
    -(void)    simplify;
    -(void)    getInput;
    -(void)    subtract: (Fraction *) f;
    
    @end
    The @implementation file:
    Code:
    #import "Fraction.h"
    
    
    @implementation Fraction
    
    @synthesize numerator, denominator;
    
    
    -(void) print  //print the fraction
    {
    	NSLog(@"%i/%i", numerator, denominator);
    }
    -(double) convertToNum //not sure what this does, but he used it in the book
    {
    	if(denominator != 0)
    		return (double) numerator/denominator;
    	else
    		return 1.0;
    }
    -(void) setTo: (int) n over: (int) d //manually set the value of the numerator and denominator 
    {
    	numerator = n;
    	denominator = d;
    }
    -(void) add: (Fraction *) f //add 2 fractions
    {
    //algorithm (it is correct)
    	numerator = numerator * f.denominator + denominator * f.numerator;
    	denominator = denominator * f.denominator;
    	[self simplify];
    }
    -(void) simplify //simplifies the fractions
    {
    	int d = 0;
    	int e = numerator;
    	double wholeNumber;
    	int n = denominator;
    	
                    while (n != 1) {
                    //simplifies fractions
    		if(denominator != 1 && denominator % n == 0 && numerator % n == 0) {
    			denominator = denominator / n;
    			numerator = numerator / n;
    		} else --n;
    	}
            //identifies improper fractions and makes them mixed numbers
    	if(numerator > denominator) {
    		NSLog(@"Improper fraction");
    		numerator = numerator % denominator;
    		do{
    			e = numerator - denominator;
    			++wholeNumber;
    		} while(e - denominator > denominator);
    		do{
    			++d;
    		}
    		while(d < wholeNumber);
    		wholeNumber = d;
    		
    		NSLog(@"%f %i/%i", wholeNumber, numerator, denominator);
    	} else NSLog(@"%i/%i", numerator, denominator);
    }
    
    
    -(void) getInput
    {
            //gets fraction input from user
    	int usrNum, usrDen;
    	scanf("%i/%i", &usrNum, &usrDen);
    	numerator = usrNum;
    	denominator = usrDen;
    }
    -(void) subtract: (Fraction *) f //subtracts fraction PROBLEM HERE!
    {
    		//makes fractions have same denominator
                    if(denominator != f.denominator) {
    		denominator = denominator * f.denominator;
    		f.denominator = f.denominator * denominator;
    		numerator = numerator * denominator;
    		f.numerator = f.numerator * f.denominator;
    	}
    //checks if solution will be a negative (still working on this this is what I'm, trying to figure out!!)
    	if(numerator / denominator < f.numerator / f.denominator) {
    		numerator = f.numerator - numerator;
    		denominator = denominator;
    	} else {
    	numerator = numerator - f.numerator;
    	denominator = denominator;
    	}
    		[self simplify];
    
    }
    @end
    
    The main file
    Code:
    #import "Fraction.h"
    
    
    int main (int argc, const char * argv[]) {
        NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
    	Fraction *myFraction = [[Fraction alloc] init];
    	Fraction *myFraction2 = [[Fraction alloc] init];
    	char operator;
    	NSLog(@"Would you like to add or substract? (enter '-' for subtraction and '+' for addition)");
    	scanf("%c", &operator);
    	//checks whether user wants to add or subtract
            switch (operator) {
    		case '+':
    			//adds
                            NSLog(@"Please print your first fraction in the format num/den.");
    			[myFraction getInput];
    			NSLog(@"Please print your second fraction in the format num/den.");
    			[myFraction2 getInput];
    			[myFraction print];
    			NSLog(@"+");
    			[myFraction2 print];
    			NSLog(@"=");
    			[myFraction add: myFraction2];
    			break;
    		case '-':
    			//subtracts
                            NSLog(@"Please print your first fraction in the format num/den.");
    			[myFraction getInput];
    			NSLog(@"Please print your second fraction in the format num/den.");
    			[myFraction2 getInput];
    			[myFraction print];
    			NSLog(@"-");
    			[myFraction2 print];
    			NSLog(@"=");
    			[myFraction subtract: myFraction2];
    			break;
    	}
    	[myFraction release];
    	[myFraction2 release];
    	
        [pool drain];
        return 0;
    }
    I know that's a TON so if ANYONE can offer words of advice, or a basic way to clean up the coding, please help! Thanks!!!

    -iMaster
     
  2. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #2
    Code:
    -(void) subtract: (Fraction *) f //subtracts fraction PROBLEM HERE!
    {
    		//makes fractions have same denominator
                    if(denominator != f.denominator) {
    		denominator = denominator * f.denominator;
    		f.denominator = f.denominator * denominator;
    		numerator = numerator * denominator;
    		f.numerator = f.numerator * f.denominator;
    	}
    //checks if solution will be a negative (still working on this this is what I'm, trying to figure out!!)
    	if(numerator / denominator < f.numerator / f.denominator) {
    		numerator = f.numerator - numerator;
    		denominator = denominator;
    	} else {
    	numerator = numerator - f.numerator;
    	denominator = denominator;
    	}
    		[self simplify];
    
    }
    
    So this is the code we care about...

    There's quite a bit going on here:
    Code:
    if(denominator != f.denominator) {
    		denominator = denominator * f.denominator;
    		f.denominator = f.denominator * denominator;
    		numerator = numerator * denominator;
    		f.numerator = f.numerator * f.denominator;
    }
    So if the denominators are different, you want to get a common denominator. Your strategy is to simply multiply each fraction by the others denominator over itself. So it SHOULD look something like:
    Code:
    if(denominator != f.denominator) {
      numerator *= f.denominator;
      f.numerator *= denominator;
      denominator *= f.denominator;
      f.denominator = denominator;
    }
    
    If you calculate a new denominator first, then use it to multiply by a numerator, you aren't going to get the correct result. Note that this is changing f that is passed in. if desired, you could set up a new Fraction to use when doing this, or call simplify on f after your calculation, though this seems a bit wasteful. You could also just set up a temporary numerator, as it is all you will care about for your calculation. if the denominators are equal, you can just assign f.numerator to this temporary variable, otherwise you can assign f.numerator*denominator to this value. You will still alter your members numerator and denominator after this. That would look something like:
    Code:
    int tempNumerator = 0;
    if(f.denominator == denominator) {
      tempNumerator = f.numerator;
    } else {
      tempNumerator = f.numerator * denominator;
      numerator *= f.denominator;
      denominator *= f.denominator;
    }
    
    Next, there's this:
    Code:
    	if(numerator / denominator < f.numerator / f.denominator) {
    		numerator = f.numerator - numerator;
    		denominator = denominator;
    	} else {
    		numerator = numerator - f.numerator;
    		denominator = denominator;
    	}
    
    There's no compelling reason, as far as i can tell, to do this. Instead:
    Code:
    numerator -= f.numerator;
    
    or
    Code:
    numerator -= tempNumerator;
    
    if you used the temp as described above.

    This might result in a negative numerator. That is OK.

    Since numerator and denominator are integers, when you are doing division as you did in your condition, this is integer division. This is essentially division with no remainder, so the result will only be the whole portion of the result. With proper fractions, this will always yield 0.

    I didn't review the rest of the code, but see if this gets you started.

    -Lee
     
  3. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #3
    IDK if this helps, but here's what happens in the Console:

    Code:
    [Session started at 2009-08-31 22:23:22 -0500.]
    2009-08-31 22:23:22.482 FractionTest[9394:903] Would you like to add or substract? (enter '-' for subtraction and '+' for addition)
    -
    2009-08-31 22:23:25.451 FractionTest[9394:903] Please print your first fraction in the format num/den.
    7/8
    2009-08-31 22:23:32.132 FractionTest[9394:903] Please print your second fraction in the format num/den.
    23/24
    2009-08-31 22:23:35.003 FractionTest[9394:903] 7/8
    2009-08-31 22:23:35.004 FractionTest[9394:903] -
    2009-08-31 22:23:35.004 FractionTest[9394:903] 23/24
    2009-08-31 22:23:35.005 FractionTest[9394:903] =
    2009-08-31 22:23:35.005 FractionTest[9394:903] Improper fraction
    2009-08-31 22:23:35.006 FractionTest[9394:903] 1.000000 0/1
    
    The Debugger has exited with status 0.
    I ALWAYS get 1.000000 0/1 when I do subtraction that results in a negative number, this is what I'm trying to fix.

    Thanks!!

    -iMaster
     
  4. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #4
    Code:
    -(void) simplify //simplifies the fractions
    {
    	int d = 0;
    	int e = numerator;
    	double wholeNumber;
    	int n = denominator;
    	
                    while (n != 1) {
                    //simplifies fractions
    		if(denominator != 1 && denominator % n == 0 && numerator % n == 0) {
    			denominator = denominator / n;
    			numerator = numerator / n;
    		} else --n;
    	}
            //identifies improper fractions and makes them mixed numbers
    	if(numerator > denominator) {
    		NSLog(@"Improper fraction");
    		numerator = numerator % denominator;
    		do{
    			e = numerator - denominator;
    			++wholeNumber;
    		} while(e - denominator > denominator);
    		do{
    			++d;
    		}
    		while(d < wholeNumber);
    		wholeNumber = d;
    		
    		NSLog(@"%f %i/%i", wholeNumber, numerator, denominator);
    	} else NSLog(@"%i/%i", numerator, denominator);
    }
    
    So now to simplify:
    One: Why is wholeNumber a double and not an int? What is all this jazz with using d to get an int, instead of using an int in the first place.
    Two: At this point, it may be easier to get a temporary value that is the absolute value of your numerator, and track if the original numerator is less than 0. After the very beginning when you find the GCD and divide through, you don't want to modify numerator or denominator, as you have done the simplification. Using a temp variable with the absolute value of the numerator instead should let you keep your current algorithm in tact without modifying numerator, etc.. At the end, you'd just print - if numerator is < 0, and nothing otherwise.

    -Lee
     
  5. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #5
    I can answer your first question Lee, wholeNumber is a double because when it was an int, it always came out to a really long, random negative integer, but when it was a double it seemed to fix this. Even so, I still sometimes got a decimal with a double so d fixes this. It goes up in increments of 1 until it is greater than wholeNumber, and then it transfers that value to wholeNumber.
    As for your second question, could you run that by me again? :eek: I'm a little confused, lol, sorry noob here.

    Hope that makes sense, thanks!!

    -iMaster
     
  6. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #6
    I'm just going to make some minor tweaks to your code, but ultimately it would be of value to have a more efficient GCD algorithm available.

    Code:
    -(void) simplify //simplifies the fractions
    {
            int wholeNumber = 0;
            int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);
    
            //Replace this with Euclid's Algorithm
            while (n != 1) {
                    //simplifies fractions
                    if(denominator != 1 && denominator % n == 0 && numerator % n == 0) {
                            denominator = denominator / n;
                            numerator = numerator / n;
                            break;
                    } else --n;
            }
            //identifies improper fractions and makes them mixed numbers
            int tmpNum = abs(numerator);
            int mult = tmpNum != numerator ? -1 : 1;
            if(tmpNum > denominator) {
                    NSLog(@"Improper fraction");
                    tmpNum = tmpNum % denominator;
                    int e = 0;
                    do{
                            e = tmpNum - denominator;
                            ++wholeNumber;
                    } while(e - denominator > denominator);
                    wholeNumber *= mult;
                    NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
            } else NSLog(@"%i/%i", numerator, denominator);
    }
    
    I switched this over to C because I'm not at a Mac, and just subbed back in NSLog for printf. I tried this for a number of things, including negatives, etc. and it seems to do what you want. I left your algorithms intact, but it's fairly easy to just do:
    wholeNumber = numerator / denominator;
    tmpNum = numerator % denominator;
    instead of the loop with substraction. As for the GCD... again, C implementations of Euclid's Algorithm are readily available if you google around, and it would be far more efficient.

    -Lee
     
  7. skochan macrumors regular

    Joined:
    Apr 1, 2006
    Location:
    California
    #7
    Thanks Lee for all the help here. There is a reduce method for the Fraction class shown in the book that uses the Euclidean algorithm to find the GCD. I'm not sure why it's not being used here.

    Cheers,

    Steve Kochan
     
  8. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #8
    Here is the new simplify method, with your changes and Euclid's algorithim (the reason I didn't use it in the first place is because I wanted to see if I could make a simplify method myself :eek:)

    Code:
    -(void) simplify
    {
    	int wholeNumber = 0;
    	int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);
    	int u = numerator;
    	int v = denominator;
    	int temp;
    	while (v != 0) {
    		temp = u % v;
    		u = v;
    		v = temp;
    	}
    	numerator /= u;
    	denominator /= u;
    	int tmpNum = abs(numerator);
    	int mult = tmpNum != numerator ? -1 : 1;
    	if(tmpNum > denominator) {
    		NSLog(@"Improper fraction");
    		tmpNum = tmpNum % denominator;
    		int e = 0;
    		do{
    			e = tmpNum - denominator;
    			++wholeNumber;
    		} while(e - denominator > denominator);
    		wholeNumber *= mult;
    		NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
    	} else NSLog(@"%i/%i", numerator, denominator);
    }
    And the subtract method:
    Code:
    -(void) subtract: (Fraction *) f
    {
    		if(denominator != f.denominator) {
    		denominator *= f.denominator;
    		f.denominator *= denominator;
    		numerator *= denominator;
    		f.numerator *= f.denominator;
    	}
    	if(numerator / denominator < f.numerator / f.denominator) {
    		numerator = f.numerator - numerator;
    		denominator = denominator;
    	} else {
    	numerator -= f.numerator;
    	}
    		[self simplify];
    
    }
    Now subtraction is not working when the two fractions don't have the same denominator, this is what I get in the console, when trying to subtract 5/6 and 1/3 (which should equal 1/2 when simplified)
    Code:
    2009-09-01 10:23:44.269 FractionTest[10994:903] Would you like to add or substract? (enter '-' for subtraction and '+' for addition)
    -
    2009-09-01 10:23:46.533 FractionTest[10994:903] Please print your first fraction in the format num/den.
    5/6
    2009-09-01 10:23:49.557 FractionTest[10994:903] Please print your second fraction in the format num/den.
    1/3
    2009-09-01 10:23:51.901 FractionTest[10994:903] 5/6
    2009-09-01 10:23:51.902 FractionTest[10994:903] -
    2009-09-01 10:23:51.903 FractionTest[10994:903] 1/3
    2009-09-01 10:23:51.903 FractionTest[10994:903] =
    2009-09-01 10:23:51.905 FractionTest[10994:903] Improper fraction
    2009-09-01 10:23:51.906 FractionTest[10994:903] 1 0/1
    Thanks for all of the help!!!

    -iMaster
     
  9. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #9
    Also could you explain some of the things going on here, for example:
    Code:
    int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);
    You are using the conditional operator here, but what is the "abs" for?

    Also, I'm not sure where to put "n" in my code? should it replace a variable? ("u" or "v" possibly?)

    Thanks!

    -iMaster
     
  10. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #10
    I posted this above:
    Code:
    if(denominator != f.denominator) {
      numerator *= f.denominator;
      f.numerator *= denominator;
      denominator *= f.denominator;
      f.denominator = denominator;
    }
    
    Compare this to your new version:
    Code:
    	if(denominator != f.denominator) {
    		denominator *= f.denominator;
    		f.denominator *= denominator;
    		numerator *= denominator;
    		f.numerator *= f.denominator;
    	}
    
    The order is very important here. If you change the denominator(s) first, bad things are going to happen. Note, though, that either of these is changing the fraction that is passed in. I consider this a bad thing. I suggested this:
    Code:
    int tempNumerator = 0;
    if(f.denominator == denominator) {
      tempNumerator = f.numerator;
    } else {
      tempNumerator = f.numerator * denominator;
      numerator *= f.denominator;
      denominator *= f.denominator;
    }
    
    instead. tempNumerator would be used as the value passed in.

    Also, you still have the code that doesn't allow for a negative numerator, and i don't understand why.

    abs takes the absolute value of an int. I did the calculation of n to accommodate for the algorithm you used to find the GCD. You wouldn't very well want to end up with a negative value, and subtract forever (or until an underflow and wrap-around). If you're using Euclid's, you shouldn't need it.

    -Lee
     
  11. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #11
    Ok, I was thinking through the multiplication incorrectly, my mistake. Fixed now.

    It seems to be working now (except negatives, obviously), so what part am I disallowing negative numbers in? Is it this part?
    Code:
    	int tempNumerator = 0;
    	if(f.denominator == denominator) {
    		tempNumerator = f.numerator;
    	} else {
    		tempNumerator = f.numerator * denominator;
    		numerator *= f.denominator;
    		denominator *= f.denominator;
    	}
    	if(numerator / denominator < f.numerator / f.denominator) {
    		numerator = f.numerator - numerator;
    		denominator = denominator;
    	} else {
    	numerator -= tempNumerator;
    I changed
    Code:
    numerator -= tempNumerator;
    from
    Code:
    numerator -= f.numerator;
    I know I sound like a complete idiot right now, but I really do appreciate the help! :D:D:D

    -iMaster
     
  12. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #12
    Just do:
    numerator -= tempNumerator;
    unconditionally.

    Your condition is using integer division anyway, so you're not likely to get what you expect from it.

    If it ends up negative, so be it. If you want to just get the difference and not actually subtract, you could do it how you have it set up now, but you'd just need to compare numerator to tempNumerator at that point, not do division in your condition.

    -Lee
     
  13. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #13
    What do you mean "unconditionally"?

    What do I need to change to make this work?
    Code:
    -(void) simplify
    {
    	int wholeNumber = 0;
    //where should I put the following int in the program???	
    int n = abs(denominator) > abs(numerator) ? abs(numerator) : abs(denominator);
    	int u = numerator;
    	int v = denominator;
    	int temp;
    	while (v != 0) {
    		temp = u % v;
    		u = v;
    		v = temp;
    	}
    	numerator /= u;
    	denominator /= u;
    	int tmpNum = abs(numerator);
    	int mult = tmpNum != numerator ? -1 : 1;
    	if(tmpNum > denominator) {
    		NSLog(@"Improper fraction");
    		tmpNum = tmpNum % denominator;
    		int e = 0;
    		do{
    			e = tmpNum - denominator;
    			++wholeNumber;
    		} while(e - denominator > denominator);
    		wholeNumber *= mult;
    		NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
    	} else NSLog(@"%i/%i", numerator, denominator);
    }
    
    
    
    -(void) subtract: (Fraction *) f
    {
    	int tempNumerator = 0;
    	if(f.denominator == denominator) {
    		tempNumerator = f.numerator;
    	} else {
    		tempNumerator = f.numerator * denominator;
    		numerator *= f.denominator;
    		denominator *= f.denominator;
    		//I know you didn't have this line in your example, and I'll take it out if necessary, but f.denominator would also have to change, not just denominator (the denominators both need to be multiplied by each other)
                    f.denominator *= denominator;
    	}
    //this part doesn't seem to be working..(this is how I'm getting it to identify problems with a negative solution, eventually I'll use a boolean so that if "negative" is true, then the solution will print with a negative sign in front of it)
    	if(numerator / denominator < f.numerator / f.denominator) {
    		numerator = f.numerator - numerator;
    	}
    // Is this what you meant by unconditionally? (take it out of the loop)
    	numerator -= tempNumerator;
    	[self simplify];
    }
    THANK YOU SO MUCH! I know how annoying I'm being right now :D:D

    -iMaster
     
  14. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #14
    Here's what I came up with:
    Code:
    -(void) simplify
    {
    	int wholeNumber = 0;
    	int u = numerator;
    	int v = denominator;
    	int temp;
    	while (v != 0) {
    		temp = u % v;
    		u = v;
    		v = temp;
    	}
    	u = abs(u);
    	numerator /= u;
    	denominator /= u;
    
    	int tmpNum = abs(numerator);
    	int mult = tmpNum != numerator ? -1 : 1;
    	if(tmpNum > denominator) {
    		NSLog(@"Improper fraction");
    		wholeNumber = tmpNum / denominator;
    		tmpNum = tmpNum % denominator;
    		wholeNumber *= mult;
    		NSLog(@"%i %i/%i", wholeNumber, tmpNum, denominator);
    	} else NSLog(@"%i/%i", numerator, denominator);
    }
    
    
    
    -(void) subtract: (Fraction *) f
    {
    	int tempNumerator = 0;
    	if(f.denominator == denominator) {
    		tempNumerator = f.numerator;
    	} else {
    		tempNumerator = f.numerator * denominator;
    		numerator *= f.denominator;
    		denominator *= f.denominator;
    	}
    	numerator -= tempNumerator;
    	[self simplify];
    }
    
    The changes were:
    n is no longer needed, as noted above.

    Don't change f. When you're subtracting one fraction from the other, there's no reason to modify the right hand operand. You know what the common denominator is, you don't need to put that in f.denominator, you just need to know what the numerator of f would be if you did make this change.

    Unconditionally just means without a condition, so no if needed.

    An abs was thrown into simplify, so you'd end up with a negative numerator instead of a negative denominator.

    The conversion to a whole number and fractional part was changed as i mentioned above, to use / and % instead of a series of subtractions.

    There may have been a few other changes, but that's what i recall changing.

    -Lee
     
  15. iMasterWeb thread starter macrumors regular

    Joined:
    Mar 15, 2009
    #15
    Thank you sooooooo much! You've been a HUGE help! Its working perfectly now!!!
     
  16. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #16
    No problem. Now change getInput and setTo:n over:d to handle signs properly... i.e. if -2/-3 is entered, change this to 2/3... if 2/-3 move the sign to the numerator. Basically, ensure that the denominator is always positive, and the numerator carries the sign.

    -Lee
     

Share This Page