10
\$\begingroup\$

I recently started learning Swift through Stanford iOS8 Course. The first assignment is to add some features to simple Calculator which was presented during lectures.

List of tasks:

  • add button for entering decimal point (appendDot())

  • add sin, cos and Pi operations

  • add Label which shows history of performed operations (history and addToHistory(recordToAddToHistory: String))

  • add clear button which clears both 'screens' and sets Calculator to the same state as at app startup (clear())

  • add backspace button (undo()) and button for changing sign of input (changeSign())

I would be very grateful if you would look at my code and give me some feedback about it.

I have doubts about the way I have implemented logic for decimal point button. In hints for the assignment is written that one can use rangeOfString(substring: String) method, but I don't see how it applies to given problem. Also there is a hint, that whole logic can be a one-liner.

ViewController.swift:

import UIKit
class ViewController: UIViewController {
 @IBOutlet weak var display: UILabel!
 @IBOutlet weak var history: UILabel!
 @IBOutlet weak var changeSignButton: UIButton!
 var userIsInTheMiddleOfTypingANumber: Bool = false
 var userInsertedADot: Bool = false
 let brain = CalculatorBrain()
 @IBAction func appendDigit(sender: UIButton) {
 let digit = sender.currentTitle!
 if userIsInTheMiddleOfTypingANumber {
 display.text = display.text! + digit
 }else{
 display.text = digit
 userIsInTheMiddleOfTypingANumber = true
 }
 addToHistory(digit)
 }
 @IBAction func appendDot() {
 if !userInsertedADot && userIsInTheMiddleOfTypingANumber {
 display.text = display.text! + "."
 userInsertedADot = true
 addToHistory(".")
 }
 if !userInsertedADot && !userIsInTheMiddleOfTypingANumber {
 display.text = "."
 userInsertedADot = true
 userIsInTheMiddleOfTypingANumber = true
 addToHistory(".")
 }
 }
 @IBAction func operate(sender: UIButton) {
 if userIsInTheMiddleOfTypingANumber {
 enter()
 }
 if let operation = sender.currentTitle {
 if let result = brain.performOperation(operation) {
 displayValue = result
 } else {
 displayValue = 0
 }
 addToHistory(operation)
 }
 }
 @IBAction func clear() {
 display.text = "0"
 history.text = " "
 brain.clearOperationStack()
 userIsInTheMiddleOfTypingANumber = false
 userInsertedADot = false
 }
 @IBAction func undo() {
 if count(display.text!) > 1 {
 display.text = dropLast(display.text!)
 }else{
 display.text = "0"
 }
 }
 @IBAction func changeSign() {
 if userIsInTheMiddleOfTypingANumber {
 if Array(display.text!)[0] == "-" {
 display.text = dropFirst(display.text!)
 }else {
 display.text = "-" + display.text!
 }
 }else {
 operate(changeSignButton)
 }
 }
 @IBAction func enter() {
 userIsInTheMiddleOfTypingANumber = false
 userInsertedADot = false
 if let result = brain.pushOperand(displayValue) {
 displayValue = result
 } else {
 displayValue = 0
 }
 addToHistory(" ")
 }
 var displayValue: Double {
 get {
 return NSNumberFormatter().numberFromString(display.text!)!.doubleValue
 }
 set {
 display.text = "\(newValue)"
 userIsInTheMiddleOfTypingANumber = false
 }
 }
 func addToHistory(recordToAddToHistory: String) {
 history.text = history.text! + recordToAddToHistory
 }
}

CalculatorBrain.swift:

import Foundation
class CalculatorBrain {
 private enum Op: Printable {
 case Operand(Double)
 case UnaryOperation(String, Double -> Double)
 case BinaryOperation(String, (Double, Double) -> Double)
 case EmptyOperation(String, Double)
 var description: String {
 get {
 switch self {
 case Operand(let operand):
 return "\(operand)"
 case .UnaryOperation(let symbol, _):
 return symbol
 case .BinaryOperation(let symbol, _):
 return symbol
 case .EmptyOperation(let symbol, _):
 return symbol
 }
 }
 }
 }
 private var opStack = [Op]()
 private var knownOps = [String:Op]()
 init() {
 func learnOp(op: Op) {
 knownOps[op.description] = op
 }
 learnOp(Op.BinaryOperation("×ばつ", *))
 learnOp(Op.BinaryOperation("÷"){1ドル / 0ドル})
 learnOp(Op.BinaryOperation("+", +))
 learnOp(Op.BinaryOperation("−") {1ドル - 0ドル})
 learnOp(Op.UnaryOperation("√", sqrt))
 learnOp(Op.UnaryOperation("sin", sin))
 learnOp(Op.UnaryOperation("cos", cos))
 learnOp(Op.EmptyOperation("π", M_PI))
 learnOp(Op.UnaryOperation("±"){-0ドル})
 }
 private func evaluate(ops: [Op]) -> (result: Double?, remainingOps: [Op]) {
 if !ops.isEmpty {
 var remainingOps = ops
 let op = remainingOps.removeLast()
 switch op {
 case .Operand(let operand):
 return (operand, remainingOps)
 case .UnaryOperation(_, let operation):
 let operandEvaluation = evaluate(remainingOps)
 if let operand = operandEvaluation.result {
 return (operation(operand), operandEvaluation.remainingOps)
 }
 case .BinaryOperation(_, let operation):
 let op1Evaluation = evaluate(remainingOps)
 if let operand1 = op1Evaluation.result {
 let op2Evaluation = evaluate(op1Evaluation.remainingOps)
 if let operand2 = op2Evaluation.result {
 return (operation(operand1, operand2), op2Evaluation.remainingOps)
 }
 }
 case .EmptyOperation(_, let operation):
 return (operation, remainingOps)
 }
 }
 return (nil, ops)
 }
 func evaluate() -> Double? {
 let (result, remainder) = evaluate(opStack)
 println("\(opStack) = \(result) with \(remainder) left over")
 return result
 }
 func pushOperand(operand: Double) -> Double? {
 opStack.append(Op.Operand(operand))
 return evaluate()
 }
 func performOperation(symbol: String) -> Double? {
 if let operation = knownOps[symbol] {
 opStack.append(operation)
 }
 return evaluate()
 }
 func clearOperationStack(){
 opStack = [Op]()
 }
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked May 11, 2015 at 4:05
\$\endgroup\$
5
  • \$\begingroup\$ Link to course? \$\endgroup\$ Commented May 11, 2015 at 5:17
  • 1
    \$\begingroup\$ The course is available on iTunes U itunes.apple.com/us/course/developing-ios-8-apps-swift/… \$\endgroup\$ Commented May 11, 2015 at 5:19
  • \$\begingroup\$ Does this course allow you to post assignment solutions publicly online? Most of the online courses I've taken prohibit that. I tried to look into it, but without iTunes installed, I can't. \$\endgroup\$ Commented May 28, 2015 at 12:43
  • \$\begingroup\$ @GlenPeterson I'm not really sure, but I don't think it is prohibited. It is not MOOC like Coursera or edX. Those are just videos and materials from Stanfords course. You don't have any peer review or due dates there. \$\endgroup\$ Commented May 28, 2015 at 12:50
  • \$\begingroup\$ does anyone know where we can find solution to those assignments? \$\endgroup\$ Commented Mar 25, 2016 at 0:54

2 Answers 2

1
\$\begingroup\$

You wrote:

In hints for the assignment is written that one can use rangeOfString(substring: String) method, but I don't see how it applies to given problem.

Instead of creating a new @IBAction it is possible to put the code for the decimal point in the existing @IBAction func appendDigit. Then rangeOfString(substring: String) can be used to determine whether or not there is already a decimal point (instead of your userInsertedADot boolean).

Eg - the test for whether there was already a floating point would then be:

if (display.text!.rangeOfString(".") != nil)

So you could incorporate something like:

if userIsInTheMiddleOfTypingANumber {
 if (digit == ".")
 && (display.text!.rangeOfString(".") != nil) {
 return // don't do anything if there is already a "."
 } else etc ...

(You might alternatively use if (display.text!.rangeOfString(".") == nil) depending how you approach the rest of the function. If there is not already a decimal point then you want it added to display.text just as if it were a digit.

Also:-

You wrote:

add Label which shows history of performed operations (history and addToHistory(recordToAddToHistory: String))

Looking at the assignment - the 2015 version which I am looking at does not seem to say anything about addToHistory(recordToAddToHistory: String. I am fairly certain that at this stage of the course the history should be derived instead by adding a function to class CalculatorBrain which returns the history by returning the CalculatorBrain's stack as a string. You could then call that function from your View Controller. Eg - something like:

func history() -> String {return " ".join(opStack.map{"\(0ドル)"})

EDIT: Site member Schule has noted that the Swift syntax has changed slightly since this answer was submitted, joinWithSeperator is now the accepted way to convert an array into a string with a separator.

answered May 27, 2015 at 14:01
\$\endgroup\$
1
  • \$\begingroup\$ Hi, thank you for your reply! Now I see how can I use rangeOfString :). I know that the assignment does not say anything about addToHistory method. I just wanted to indicate which part of my code implements history feature. \$\endgroup\$ Commented May 31, 2015 at 11:14
1
\$\begingroup\$

I'm working on the same assignment and my solution for solving the decimal problem is shown below. It is contained within the touchDigit function:

@IBAction func touchDigit(_ sender: UIButton) {
 let digit = sender.currentTitle!
 if userIsInTheMiddleOfTyping {
 if digit == "." && display.text!.contains(".") {
 return
 } else {
 let textCurrentlyInDisplay = display.text!
 display.text = textCurrentlyInDisplay + digit
 }
 }
 else {
 if digit == "." {
 display.text = "0" + digit
 } else {
 display.text = digit
 }
 userIsInTheMiddleOfTyping = true
 }
}
chicks
2,8593 gold badges18 silver badges30 bronze badges
answered Oct 27, 2017 at 10:20
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.