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
andaddToHistory(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]()
}
}
-
\$\begingroup\$ Link to course? \$\endgroup\$Legato– Legato2015年05月11日 05:17:44 +00:00Commented 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\$Sebastian Osiński– Sebastian Osiński2015年05月11日 05:19:52 +00:00Commented 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\$GlenPeterson– GlenPeterson2015年05月28日 12:43:55 +00:00Commented 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\$Sebastian Osiński– Sebastian Osiński2015年05月28日 12:50:20 +00:00Commented May 28, 2015 at 12:50
-
\$\begingroup\$ does anyone know where we can find solution to those assignments? \$\endgroup\$Thor– Thor2016年03月25日 00:54:47 +00:00Commented Mar 25, 2016 at 0:54
2 Answers 2
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.
-
\$\begingroup\$ Hi, thank you for your reply! Now I see how can I use
rangeOfString
:). I know that the assignment does not say anything aboutaddToHistory
method. I just wanted to indicate which part of my code implements history feature. \$\endgroup\$Sebastian Osiński– Sebastian Osiński2015年05月31日 11:14:35 +00:00Commented May 31, 2015 at 11:14
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
}
}
Explore related questions
See similar questions with these tags.