2726. Calculator with Method Chaining
Problem Description
This problem asks you to implement a Calculator
class that supports basic mathematical operations and method chaining.
The Calculator
class should:
-
Initialize with a starting value: The constructor takes a number that becomes the initial
result
value. -
Support five mathematical operations, each returning the
Calculator
instance for chaining:add(value)
: Addsvalue
to the current resultsubtract(value)
: Subtractsvalue
from the current resultmultiply(value)
: Multiplies the current result byvalue
divide(value)
: Divides the current result byvalue
(must throw error "Division by zero is not allowed" ifvalue
is 0)power(value)
: Raises the current result to the power ofvalue
-
Provide a way to retrieve the result: The
getResult()
method returns the current calculated value. -
Enable method chaining: All operation methods return
this
(the Calculator instance), allowing consecutive operations like:calculator.add(5).multiply(2).subtract(3).getResult()
The solution uses a private property x
to store the current result. Each operation method modifies x
and returns this
to enable chaining. The divide
method includes validation to prevent division by zero. The getResult
method simply returns the current value of x
.
Results within 10^-5
of the actual answer are considered correct, accounting for floating-point precision issues.
Intuition
The key insight for this problem is recognizing that method chaining requires each method to return the object itself. When we call calculator.add(5).multiply(2)
, the add(5)
method must return the calculator object so that we can immediately call multiply(2)
on it.
To achieve this pattern, we need:
-
State preservation: A single variable to hold the running result throughout all operations. This is why we use a private property
x
to maintain the current value across method calls. -
Return
this
pattern: Each operation method performs its calculation on the stored value and then returnsthis
(the current instance). This is the fundamental pattern that enables method chaining - by returning the object itself, we allow the next method to be called on the same object. -
Mutating operations: Instead of creating new Calculator instances, we modify the existing instance's state. This makes sense because we want a continuous calculation flow where each operation builds upon the previous result.
The design follows the Fluent Interface pattern, commonly used in builder patterns and query builders. The idea is to make the code read more naturally, almost like a sentence: "take this number, add 5, multiply by 2, subtract 3, and give me the result."
For the division by zero check, we handle this explicitly before performing the division since JavaScript would return Infinity
for division by zero, which isn't the desired behavior. By throwing an error with the exact message required, we prevent invalid mathematical operations while maintaining the chainable interface (the error stops the chain execution).
The getResult()
method breaks the chain by returning a number instead of the Calculator instance, which makes logical sense as it's the terminating operation to retrieve our final answer.
Solution Approach
The implementation uses a class-based approach with a single private property to maintain state and instance methods that return this
for chaining.
Core Structure:
-
Private property
x
: Stores the current calculation result. Using a private property ensures encapsulation and prevents external modification. -
Constructor: Initializes the
x
property with the provided starting value:constructor(value: number) { this.x = value; }
Implementation of Each Operation:
Each arithmetic method follows the same pattern:
- Perform the operation on
this.x
- Return
this
to enable chaining
-
Addition:
this.x += value
adds the value to the current result -
Subtraction:
this.x -= value
subtracts the value from the current result -
Multiplication:
this.x *= value
multiplies the current result by the value -
Division:
divide(value: number): Calculator { if (value === 0) { throw new Error('Division by zero is not allowed'); } this.x /= value; return this; }
First checks for zero to prevent invalid operations, then performs
this.x /= value
-
Power:
this.x **= value
raises the current result to the given power using the exponentiation operator
Result Retrieval:
The getResult()
method simply returns the current value of x
without modifying it:
getResult(): number {
return this.x;
}
Method Chaining Mechanism:
By returning this
from each operation method, we enable consecutive method calls:
new Calculator(10) .add(5) // x becomes 15, returns this .multiply(2) // x becomes 30, returns this .divide(3) // x becomes 10, returns this .getResult() // returns 10
The solution is straightforward and efficient with O(1) time complexity for each operation and O(1) space complexity for the entire calculator instance.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a calculation step-by-step: new Calculator(2).add(5).multiply(3).subtract(4).divide(2).getResult()
Step 1: Initialize
new Calculator(2)
- Creates a new Calculator instance
- Sets internal property
x = 2
- Current state:
x = 2
Step 2: Add 5
.add(5)
- Executes:
this.x = this.x + 5 = 2 + 5 = 7
- Returns
this
(the Calculator instance) - Current state:
x = 7
Step 3: Multiply by 3
.multiply(3)
- Executes:
this.x = this.x * 3 = 7 * 3 = 21
- Returns
this
(the Calculator instance) - Current state:
x = 21
Step 4: Subtract 4
.subtract(4)
- Executes:
this.x = this.x - 4 = 21 - 4 = 17
- Returns
this
(the Calculator instance) - Current state:
x = 17
Step 5: Divide by 2
.divide(2)
- First checks: Is
value === 0
? No, proceed - Executes:
this.x = this.x / 2 = 17 / 2 = 8.5
- Returns
this
(the Calculator instance) - Current state:
x = 8.5
Step 6: Get Result
.getResult()
- Returns the current value of
x
- Final answer:
8.5
Key Points:
- Each operation method modifies the same internal variable
x
- Each operation returns
this
, allowing the next method to be called immediately - The chain continues until
getResult()
is called, which returns a number instead of the Calculator instance - If we tried
.divide(0)
at any point, it would throw an error and stop execution
Solution Implementation
1# Global variable to store the current calculator value
2calculator_value = 0
3
4def initialize_calculator(value):
5 """
6 Initializes the calculator with a starting value
7
8 Args:
9 value: The initial value for the calculator
10 """
11 global calculator_value
12 calculator_value = value
13
14def add(value):
15 """
16 Adds a value to the current calculator value
17
18 Args:
19 value: The value to add
20
21 Returns:
22 The updated calculator value
23 """
24 global calculator_value
25 calculator_value += value
26 return calculator_value
27
28def subtract(value):
29 """
30 Subtracts a value from the current calculator value
31
32 Args:
33 value: The value to subtract
34
35 Returns:
36 The updated calculator value
37 """
38 global calculator_value
39 calculator_value -= value
40 return calculator_value
41
42def multiply(value):
43 """
44 Multiplies the current calculator value by a given value
45
46 Args:
47 value: The value to multiply by
48
49 Returns:
50 The updated calculator value
51 """
52 global calculator_value
53 calculator_value *= value
54 return calculator_value
55
56def divide(value):
57 """
58 Divides the current calculator value by a given value
59
60 Args:
61 value: The value to divide by
62
63 Returns:
64 The updated calculator value
65
66 Raises:
67 ValueError: If attempting to divide by zero
68 """
69 global calculator_value
70 if value == 0:
71 raise ValueError('Division by zero is not allowed')
72 calculator_value /= value
73 return calculator_value
74
75def power(value):
76 """
77 Raises the current calculator value to a given power
78
79 Args:
80 value: The exponent value
81
82 Returns:
83 The updated calculator value
84 """
85 global calculator_value
86 calculator_value **= value
87 return calculator_value
88
89def get_result():
90 """
91 Gets the current result of the calculator
92
93 Returns:
94 The current calculator value
95 """
96 return calculator_value
97
1/**
2 * Calculator class that maintains a global state for calculator operations
3 */
4public class Calculator {
5
6 // Global variable to store the current calculator value
7 private static double calculatorValue = 0;
8
9 /**
10 * Initializes the calculator with a starting value
11 * @param value The initial value for the calculator
12 */
13 public static void initializeCalculator(double value) {
14 calculatorValue = value;
15 }
16
17 /**
18 * Adds a value to the current calculator value
19 * @param value The value to add
20 * @return The updated calculator value
21 */
22 public static double add(double value) {
23 calculatorValue += value;
24 return calculatorValue;
25 }
26
27 /**
28 * Subtracts a value from the current calculator value
29 * @param value The value to subtract
30 * @return The updated calculator value
31 */
32 public static double subtract(double value) {
33 calculatorValue -= value;
34 return calculatorValue;
35 }
36
37 /**
38 * Multiplies the current calculator value by a given value
39 * @param value The value to multiply by
40 * @return The updated calculator value
41 */
42 public static double multiply(double value) {
43 calculatorValue *= value;
44 return calculatorValue;
45 }
46
47 /**
48 * Divides the current calculator value by a given value
49 * @param value The value to divide by
50 * @return The updated calculator value
51 * @throws IllegalArgumentException if attempting to divide by zero
52 */
53 public static double divide(double value) {
54 if (value == 0) {
55 throw new IllegalArgumentException("Division by zero is not allowed");
56 }
57 calculatorValue /= value;
58 return calculatorValue;
59 }
60
61 /**
62 * Raises the current calculator value to a given power
63 * @param value The exponent value
64 * @return The updated calculator value
65 */
66 public static double power(double value) {
67 calculatorValue = Math.pow(calculatorValue, value);
68 return calculatorValue;
69 }
70
71 /**
72 * Gets the current result of the calculator
73 * @return The current calculator value
74 */
75 public static double getResult() {
76 return calculatorValue;
77 }
78}
79
1#include <stdexcept>
2#include <cmath>
3
4// Global variable to store the current calculator value
5double calculatorValue = 0.0;
6
7/**
8 * Initializes the calculator with a starting value
9 * @param value - The initial value for the calculator
10 */
11void initializeCalculator(double value) {
12 calculatorValue = value;
13}
14
15/**
16 * Adds a value to the current calculator value
17 * @param value - The value to add
18 * @return The updated calculator value
19 */
20double add(double value) {
21 calculatorValue += value;
22 return calculatorValue;
23}
24
25/**
26 * Subtracts a value from the current calculator value
27 * @param value - The value to subtract
28 * @return The updated calculator value
29 */
30double subtract(double value) {
31 calculatorValue -= value;
32 return calculatorValue;
33}
34
35/**
36 * Multiplies the current calculator value by a given value
37 * @param value - The value to multiply by
38 * @return The updated calculator value
39 */
40double multiply(double value) {
41 calculatorValue *= value;
42 return calculatorValue;
43}
44
45/**
46 * Divides the current calculator value by a given value
47 * @param value - The value to divide by
48 * @return The updated calculator value
49 * @throws std::invalid_argument if attempting to divide by zero
50 */
51double divide(double value) {
52 if (value == 0.0) {
53 throw std::invalid_argument("Division by zero is not allowed");
54 }
55 calculatorValue /= value;
56 return calculatorValue;
57}
58
59/**
60 * Raises the current calculator value to a given power
61 * @param value - The exponent value
62 * @return The updated calculator value
63 */
64double power(double value) {
65 calculatorValue = std::pow(calculatorValue, value);
66 return calculatorValue;
67}
68
69/**
70 * Gets the current result of the calculator
71 * @return The current calculator value
72 */
73double getResult() {
74 return calculatorValue;
75}
76
1// Global variable to store the current calculator value
2let calculatorValue: number = 0;
3
4/**
5 * Initializes the calculator with a starting value
6 * @param value - The initial value for the calculator
7 */
8function initializeCalculator(value: number): void {
9 calculatorValue = value;
10}
11
12/**
13 * Adds a value to the current calculator value
14 * @param value - The value to add
15 * @returns The updated calculator value
16 */
17function add(value: number): number {
18 calculatorValue += value;
19 return calculatorValue;
20}
21
22/**
23 * Subtracts a value from the current calculator value
24 * @param value - The value to subtract
25 * @returns The updated calculator value
26 */
27function subtract(value: number): number {
28 calculatorValue -= value;
29 return calculatorValue;
30}
31
32/**
33 * Multiplies the current calculator value by a given value
34 * @param value - The value to multiply by
35 * @returns The updated calculator value
36 */
37function multiply(value: number): number {
38 calculatorValue *= value;
39 return calculatorValue;
40}
41
42/**
43 * Divides the current calculator value by a given value
44 * @param value - The value to divide by
45 * @returns The updated calculator value
46 * @throws Error if attempting to divide by zero
47 */
48function divide(value: number): number {
49 if (value === 0) {
50 throw new Error('Division by zero is not allowed');
51 }
52 calculatorValue /= value;
53 return calculatorValue;
54}
55
56/**
57 * Raises the current calculator value to a given power
58 * @param value - The exponent value
59 * @returns The updated calculator value
60 */
61function power(value: number): number {
62 calculatorValue **= value;
63 return calculatorValue;
64}
65
66/**
67 * Gets the current result of the calculator
68 * @returns The current calculator value
69 */
70function getResult(): number {
71 return calculatorValue;
72}
73
Time and Space Complexity
Time Complexity: Each method (add
, subtract
, multiply
, divide
, power
, getResult
) performs a constant-time operation. The arithmetic operations and the assignment take O(1)
time. The constructor also runs in O(1)
time as it only initializes a single variable. Therefore, each individual method call has a time complexity of O(1)
.
For a chain of n
method calls (e.g., calculator.add(5).multiply(2).subtract(3)...
), the total time complexity would be O(n)
, where n
is the number of operations performed.
Space Complexity: The Calculator class stores only a single number x
as its state, regardless of how many operations are performed. The space used remains constant throughout the lifetime of the object. Each method returns this
(a reference to the same object), so no additional Calculator objects are created during method chaining. Therefore, the space complexity is O(1)
.
The method chaining pattern (fluent interface) doesn't create new objects but modifies and returns the same instance, maintaining constant space usage.
Common Pitfalls
1. Global State Management Issues
The Python implementation uses a global variable calculator_value
, which creates several problems:
- No support for multiple calculator instances: You can't have two calculators running independently
- State persistence between tests: The global value persists across different test cases, leading to incorrect results
- Thread safety concerns: Global state is not thread-safe for concurrent operations
Example of the problem:
initialize_calculator(10) add(5) # calculator_value = 15 # Start a "new" calculator initialize_calculator(20) # But if someone forgets to initialize, they get the old value!
2. Loss of Method Chaining
The provided Python solution doesn't support method chaining as required by the problem. You can't write:
# This won't work with the global function approach result = Calculator(10).add(5).multiply(2).getResult()
3. Floating-Point Precision Issues
When performing division and power operations, floating-point arithmetic can introduce precision errors that might exceed the 10^-5 tolerance:
# Example: 0.1 + 0.2 != 0.3 in floating-point calculator_value = 0.1 add(0.2) # Might not exactly equal 0.3
Solution
Here's a proper class-based implementation that addresses these pitfalls:
class Calculator:
def __init__(self, value: float):
"""Initialize calculator with a starting value"""
self._result = float(value) # Ensure float for consistency
def add(self, value: float):
"""Add value to current result"""
self._result += value
return self # Enable chaining
def subtract(self, value: float):
"""Subtract value from current result"""
self._result -= value
return self
def multiply(self, value: float):
"""Multiply current result by value"""
self._result *= value
return self
def divide(self, value: float):
"""Divide current result by value"""
if value == 0:
raise ValueError('Division by zero is not allowed')
self._result /= value
return self
def power(self, value: float):
"""Raise current result to the power of value"""
self._result **= value
return self
def getResult(self) -> float:
"""Get the current result"""
# Round to handle floating-point precision within 10^-5
return round(self._result, 10)
# Usage example with proper chaining:
calc = Calculator(10)
result = calc.add(5).multiply(2).divide(3).getResult()
print(result) # Output: 10.0
# Multiple independent instances work correctly:
calc1 = Calculator(10)
calc2 = Calculator(20)
calc1.add(5)
calc2.subtract(5)
print(calc1.getResult()) # 15.0
print(calc2.getResult()) # 15.0
Key improvements:
- Instance-based state: Each Calculator object has its own
_result
property - Method chaining support: All operation methods return
self
- Proper encapsulation: Using
_result
as a private property - Floating-point handling: Converting input to float and rounding output for precision
- Multiple instances: Can create and use multiple calculators independently
How does quick sort divide the problem into subproblems?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!