Stanford CS193P Assignment 1 Calculator Solution

I got to be honest, this Stanford CS193P RPN Calculator Assignment is not exciting me one bit. I am working through it to understand the basics, and I’ve completed it without the Extra Credit. I’m also not sure about part 6, since my calculator says “inf” if I divide by 0 already, so I’m not sure what else I need to do.

Anyways, here is my solution to at least the “meat” of this assignment:

CalculatorViewController.h

//
//  CalculatorViewController.h
//  Calculator
//
//  Created by Natalia Murashev on 12/4/11.
//  Copyright (c) 2011 Holler. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface CalculatorViewController : UIViewController

@property (weak, nonatomic) IBOutlet UILabel *display;
@property (weak, nonatomic) IBOutlet UILabel *history;

@end

CalculatorViewController.m

//
//  CalculatorViewController.m
//  Calculator
//
//  Created by Natalia Murashev on 12/4/11.
//  Copyright (c) 2011 Holler. All rights reserved.
//

#import "CalculatorViewController.h"
#import "CalculatorBrain.h"

@interface CalculatorViewController()
@property (nonatomic) BOOL userIsInTheMiddleOfEnteringANumber;
@property (nonatomic, strong) CalculatorBrain *brain;
@property (nonatomic, strong) NSMutableArray *periodPressed;
@end

@implementation CalculatorViewController

@synthesize display = _display;
@synthesize history = _history;
@synthesize userIsInTheMiddleOfEnteringANumber = _userIsInTheMiddleOfEnteringANumber;
@synthesize brain = _brain;
@synthesize periodPressed = _periodPressed;

- (CalculatorBrain *) brain
{
    if(!_brain) _brain = [[CalculatorBrain alloc] init];
    return _brain;
}

- (NSMutableArray *) periodPressed
{
    if(_periodPressed == nil)_periodPressed = [[NSMutableArray alloc] init];
    return _periodPressed;
}

- (IBAction)digitPressed:(UIButton *)sender 
{
    NSString *digit = [sender currentTitle];
    
    if(self.userIsInTheMiddleOfEnteringANumber) {
        
        //when the period is entered, it is stored in the periodPressed array.
        //if the user entered more than 1 period, it does not get added to the outlets
        if([digit isEqualToString:@"."]) {
            [self.periodPressed addObject:digit];
            if([self.periodPressed count] == 1) {
                //adds the digit to the main calculator display
                self.display.text = [self.display.text stringByAppendingString:digit]; 
                //adds the digit to the history display
                self.history.text = [self.history.text stringByAppendingString:digit]; 
            }
            
        //when the user clicks on a digit (not a period), it is added to the outlets
        } else {
            self.display.text = [self.display.text stringByAppendingString:digit];
            self.history.text = [self.history.text stringByAppendingString:digit];
        }
        
    }
    
    //when the user is not in the middle of entering a number...
    else {
        //when a period is added first, it is added to the period pressed array
        if([digit isEqualToString:@"."]) {
            [self.periodPressed addObject:digit];
        }
        //adds the initial digit to the main calculator display
        self.display.text = digit;
        //adds the digit to the history display
        if(self.history.text == nil) {
            self.history.text = digit;
        } else {
            self.history.text = [self.history.text stringByAppendingString:digit];
        }
        self.userIsInTheMiddleOfEnteringANumber = YES;
    }
    
        
}

- (IBAction)enterPressed 
{
    [self.brain pushOperand:[self.display.text doubleValue]];
    self.userIsInTheMiddleOfEnteringANumber = NO;
    [self.periodPressed removeAllObjects];
    self.history.text = [self.history.text stringByAppendingString:@" "];
    
}

- (IBAction)operationPressed:(UIButton *)sender 
{   
    if(self.userIsInTheMiddleOfEnteringANumber) [self enterPressed];  
    double result = [self.brain performOperation:sender.currentTitle];
    
    NSString *resultString = [NSString stringWithFormat:@"%g", result];
    self.display.text = resultString;
    
    self.history.text = [self.history.text stringByAppendingString:sender.currentTitle];
    self.history.text = [self.history.text stringByAppendingString:@" "];
}

- (IBAction)clearPressed:(UIButton *)sender {
    self.userIsInTheMiddleOfEnteringANumber = NO;
    self.display.text = nil; 
    self.history.text = nil; 
    [self.periodPressed removeAllObjects];
    self.brain = nil;
}

- (void)viewDidUnload {
    [self setHistory:nil];
    [super viewDidUnload];
}
@end

CalculatorBrain.h

//
//  CalculatorBrain.h
//  Calculator
//
//  Created by Natalia Murashev on 12/4/11.
//  Copyright (c) 2011 Holler. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface CalculatorBrain : NSObject

- (void)pushOperand:(double)operand;
- (double)performOperation:(NSString *)operation;

@end

CalculatorBrain.m

//
//  CalculatorBrain.m
//  Calculator
//
//  Created by Natalia Murashev on 12/4/11.
//  Copyright (c) 2011 Holler. All rights reserved.
//

#import "CalculatorBrain.h"

@interface CalculatorBrain()

@property (nonatomic, strong) NSMutableArray *operandStack;
 
@end

@implementation CalculatorBrain

@synthesize operandStack = _operandStack;

- (NSMutableArray *)operandStack
{
    if(_operandStack == nil) _operandStack = [[NSMutableArray alloc] init];
    return _operandStack;
}

- (void)pushOperand:(double)operand
{
    [self.operandStack addObject:[NSNumber numberWithDouble:operand]];
}

- (double) popOperand
{
    NSNumber *operandObject = [self.operandStack lastObject];
    if(operandObject != nil) [self.operandStack removeLastObject];
    return [operandObject doubleValue];
    
}


- (double)performOperation:(NSString *)operation
{
    double result = 0;
    
    if([operation isEqualToString:@"+"]) {
        result = [self popOperand] + [self popOperand];
    } else if([@"*" isEqualToString:operation]) {
        result = [self popOperand] * [self popOperand];
    } else if ([operation isEqualToString:@"-"]) {
        double subtrahend = [self popOperand];
        result = [self popOperand] - subtrahend;
    } else if ([operation isEqualToString:@"/"]) {
        double divisor = [self popOperand];
        result = [self popOperand] / divisor;
    } else if([operation isEqualToString:@"sin"]) {
        result = sin([self popOperand]);
    } else if([operation isEqualToString:@"cos"]) {
        result = cos([self popOperand]);
    } else if([operation isEqualToString:@"sqrt"]) {
        result = sqrt([self popOperand]);
    } else if([operation isEqualToString:@"π"]) {
        result = M_PI;
    } else if([operation isEqualToString:@"log"]) {
        result = log([self popOperand]);
    } 
    
    
    [self pushOperand:result];
    
    return result;
}


@end

Enjoy the article? Join over 17,500+ Swift developers and enthusiasts who get my weekly updates.

  • David Melo

    Great Posts! I’m taking this course on ITunes U and I like your solutions, gives me great insight. You linked to the Assignment 1 pdf, would you also happen to have a link to the walkthrough? The documentation available online is only for 2010 and its really making it tough for me 🙁

    Great blog, will definitely stay tuned for more.

  • Adnan

    Thanks for sharing. I’m trying to do this one and I got a bit stuck too. But, have you could have maybe tried this to solve your ‘inf’ problem:

    else if ([operation isEqualToString:operation]) {
    double divisor = [self popOperand];
    if (divisor) result = [self popOperand] / divisor;
    }

    I’ve yet to do Q2 onwards. I’m getting stuck on the sin. I’ve added this to my CalculatorBrain.m, but for some reason nothing happens:

    else if([operation isEqualToString:@”sin”]) {
    result = sin([self popOperand]);
    }

    • Adnan

      Just realised why I couldn’t get the sin method to work. My division method was wrong, it should be:

      else if ([operation isEqualToString:@”/”])…
      not
      else if ([operation isEqualToString:operation])…

  • Matt A

    Hello, I just finished the base part of Assignment 1. When I build it, there are no compiler errors. The calculator digits function properly, and it allows me to append numbers properly. But as soon as I press enter to add the number to the stack, the program crashes. It automatically opens the main.m file and tells me that in the @autoreleasepool a signal SIGABRT was received in thread 1. I have tried and tried to find the error in my code but have had no prevail. Any help you can offer up would be greatly appreciated!

    • Hi Matt,

      Unfortunately, I can’t help you as I decided to not continue with this class (I found it too difficult for a beginner like me) and am learning Ruby instead. Take a look at the above code and see if you could find an error. Another option is to try to go through the walkthrough again: http://www.stanford.edu/class/cs193p/cgi-bin/drupal/system/files/assignments/Walkthrough%20iOS%205_0.pdf

      Best of luck!

      • Matt A

        Thanks for your response! I just recently found the error. It was that I had linked my enter button twice and it freaked out about that. Once I figured that out it worked perfectly. Now on to the trig functions, clear button etc.

  • Hi Natasha,
    By solving task number 5 I think you have created a memory leak in your code. By doing
    self.brain = nil;
    you have only lost reference to the current calculatorBrain instance. Then you create a new one.
    In the task 5 you should implement API to your model. So you should implement a new method that will empty the NSMutableArray operandStack. Then you should call it from your clearPressed: method. You can find my solution on the link below:
    http://mobiledevvt.blogspot.com/2012/02/cs193p-fall-2011-iphone-application.html

    Hi Matt A,
    You should really add exception breakpoint. It will help you out with signal SIGABRT was received messages inside the main.m. Take a look at my post explaining hot to do this.
    http://mobiledevvt.blogspot.com/2012/02/program-received-signal-sigabrt.html

  • I am also going through the fall 2011 version on iTunesU. Here is the way I accomplished this.

    (IBAction) clearPressed {
    self.display.text = @”0″;
    self.userIsInTheMiddleOfEnteringANumber = NO;
    self.brain = nil;
    }

    The only thing is I actually did not have to add API to my model. Because the controller has a CalculatorBrain instance variable I just toss that out, and since we lazily instantiated our brain getter, I’ll get a brand new one (already cleared) next time I call the getter.

    Winfred
    http://www.agileinfoways.com/technical-expertise/mobile-applications-development/iphone/

  • CodePolicemanOfficer

    Any chance you will post the solution for swift?