Difficulty: Easy | Acceptance: 77.60% | Paid: No Topics: N/A
Design a Calculator class. The class should provide the following methods:
add(value): Adds the given value to the current result and returns the calculator instance for chaining.subtract(value): Subtracts the given value from the current result and returns the calculator instance for chaining.multiply(value): Multiplies the current result by the given value and returns the calculator instance for chaining.divide(value): Divides the current result by the given value and returns the calculator instance for chaining. If the value is 0, throw an error “Division by zero”.power(value): Raises the current result to the power of the given value and returns the calculator instance for chaining.getResult(): Returns the current result.
All methods except getResult() should support method chaining, meaning they return the calculator instance itself.
- Examples
- Constraints
- Approach 1: Class with Method Chaining
- Approach 2: Using Builder Pattern
- Approach 3: Functional Approach with Closure
Examples
Example 1
Input:
actions = ["Calculator", "add", "subtract", "getResult"],
values = [[10], [5], [7], []]
Output: 8
Explanation:
new Calculator(10) // 10
.add(5) // 10 + 5 = 15
.subtract(7) // 15 - 7 = 8
.getResult() // 8
Example 2
Input:
actions = ["Calculator", "multiply", "power", "getResult"],
values = [[2], [5], [2], []]
Output: 100
Explanation:
new Calculator(2) // 2
.multiply(5) // 2 * 5 = 10
.power(2) // 10² = 100
.getResult() // 100
Example 3
Input:
actions = ["Calculator", "divide", "add", "getResult"],
values = [[20], [0], [5], []]
Output: "Division by zero"
Explanation:
new Calculator(20) // 20
.divide(0) // Error: Division by zero
Constraints
- actions is a valid JSON array of strings
- values is a valid JSON array of numbers
- 2 <= actions.length <= 2 * 10^4
- 1 <= values.length <= 2 * 10^4 - 1
- actions[i] is one of "Calculator", "add", "subtract", "multiply", "divide", "power", and "getResult"
- First action is always "Calculator"
- Last action is always "getResult"
Approach 1: Class with Method Chaining
Intuition
Create a Calculator class with an instance variable to store the result. Each arithmetic method updates this result and returns this to enable method chaining.
Steps
- Initialize the Calculator with a starting value in the constructor
- Implement each method (add, subtract, multiply, divide, power) to modify the result
- Return
thisfrom each method to enable chaining - Implement getResult() to return the current result value
- Handle division by zero by throwing an error
class Calculator:
def __init__(self, value):
self.result = value
def add(self, value):
self.result += value
return self
def subtract(self, value):
self.result -= value
return self
def multiply(self, value):
self.result *= value
return self
def divide(self, value):
if value == 0:
raise Exception("Division by zero")
self.result /= value
return self
def power(self, value):
self.result **= value
return self
def getResult(self):
return self.resultComplexity
- Time: O(1) for each operation
- Space: O(1) - only storing the result
- Notes: Simple and intuitive approach, most commonly used in real-world scenarios
Approach 2: Using Builder Pattern
Intuition Separate the calculation logic from the result storage using a builder-like pattern where operations are queued and executed when getResult is called.
Steps
- Create a Calculator class that stores the initial value and a list of operations
- Each method adds an operation to the queue instead of modifying the result directly
- getResult() executes all operations in sequence and returns the final result
- This allows for potential operation replay or modification before execution
class Calculator:
def __init__(self, value):
self.initial = value
self.operations = []
def add(self, value):
self.operations.append(('add', value))
return self
def subtract(self, value):
self.operations.append(('subtract', value))
return self
def multiply(self, value):
self.operations.append(('multiply', value))
return self
def divide(self, value):
if value == 0:
raise Exception("Division by zero")
self.operations.append(('divide', value))
return self
def power(self, value):
self.operations.append(('power', value))
return self
def getResult(self):
result = self.initial
for op, value in self.operations:
if op == 'add':
result += value
elif op == 'subtract':
result -= value
elif op == 'multiply':
result *= value
elif op == 'divide':
result /= value
elif op == 'power':
result **= value
return resultComplexity
- Time: O(n) for getResult() where n is the number of operations, O(1) for other methods
- Space: O(n) to store all operations
- Notes: Useful when you need to replay or modify operations before execution, but uses more memory
Approach 3: Functional Approach with Closure
Intuition Use closures to encapsulate the state and return an object with methods that can access and modify the enclosed state, enabling method chaining without a traditional class.
Steps
- Create a factory function that takes an initial value
- Use a closure to maintain the result state
- Return an object with methods that modify the enclosed state
- Each method returns the object itself to enable chaining
def createCalculator(value):
result = [value]
class Calculator:
def add(self, value):
result[0] += value
return self
def subtract(self, value):
result[0] -= value
return self
def multiply(self, value):
result[0] *= value
return self
def divide(self, value):
if value == 0:
raise Exception("Division by zero")
result[0] /= value
return self
def power(self, value):
result[0] **= value
return self
def getResult(self):
return result[0]
return Calculator()Complexity
- Time: O(1) for each operation
- Space: O(1) - only storing the result in closure
- Notes: Functional programming style, provides encapsulation without classes, commonly used in JavaScript