In the second test we write, we'll show that we need to change the internal representation of the calculator.
The scenario we want to test is that when we tap on Enter, the number we just entered moves from the first to the second position: WHEN we select the two AND we tap on Enter THEN the stack first position is empty AND the second contains the number 2.
With this scenario, we want to introduce Enter as a way to add multiple operands. The test could be written as follows:
func test_TwoEnter__TwoOnSecondPlace() {
rpnCalculator.new(element: "2")
rpnCalculator.new(element: "e")
XCTAssertEqual(rpnCalculator.line0, "")
XCTAssertEqual(rpnCalculator.line1, "2")
}
We must now introduce an implementation of Stack. The Swift standard library, unlike many of the other languages, doesn't provide a default Stack implementation, however, it is easy to implement it backed with an array:
struct Stack {
private var data: [String] = []
mutating func push(_ element: String) {
data.append(element)
}
mutating func pop() -> String {
return data.popLast() ?? ""
}
subscript(idx: Int) -> String {
guard idx < data.count else {
return ""
}
return data[data.count - idx - 1]
}
}
Other than push and pop, we also need a way to inspect the value of the elements using an index, so we added a subscript. Since we push on the tail and we pop on the head, our stack is upside down, and in the subscript, we must invert the index, ensuring that we do not exceed the boundaries.
With the implemented stack, the calculator is now as follows:
class FloatRpnCalculator: RpnCalculator {
private var stack: Stack = Stack()
var line3: String {
get { return stack[3] }
}
var line2: String {
get { return stack[2] }
}
var line1: String {
get { return stack[1] }
}
var line0: String {
get { return stack[0] }
}
func new(element: String) {
if element == "e" {
stack.push("")
} else {
stack.push(element)
}
}
}
We run the tests, and voilĂ , green again:
Moving on to the third test, we want to ensure that we can add multiple operators.
This how we write the test:
func test_TwoEnterFour__FourTwoOnTheStack() {
rpnCalculator.new(element: "2")
rpnCalculator.new(element: "e")
rpnCalculator.new(element: "4")
XCTAssertEqual(rpnCalculator.line0, "4")
XCTAssertEqual(rpnCalculator.line1, "2")
}
Since we have already implemented Enter, we might be lucky, and this could just pass.
If we run the tests now, we get red.
This is how Xcode will present the failing error:
This is because we push a new element every time we add a new element, but in this case what we want is to append it to the element at the topmost place.
We need to add to our stack implementation, a feature to append a value to the topmost element:
struct Stack {
// ...
mutating func appendAtTopmost(_ element: String) {
guard !data.isEmpty else {
return push(element)
}
data[data.count - 1] += element
}
// ...
}
The calculator changes accordingly:
class FloatRpnCalculator: RpnCalculator {
// ...
func new(element: String) {
if element == "e" {
stack.push("")
} else {
stack.appendAtTopmost(element)
}
}
}