Hone logo
Hone
Problems

Python Expression Evaluator

This challenge involves building a robust expression evaluator in Python that can parse and compute the results of mathematical expressions. This is a fundamental programming task with applications ranging from simple calculators to more complex scientific computing tools and scripting languages.

Problem Description

Your task is to create a Python function, evaluate_expression(expression_string), that takes a string representing a mathematical expression and returns its computed numerical value. The evaluator should support basic arithmetic operations: addition (+), subtraction (-), multiplication (*), and division (/). It should also handle parentheses for controlling the order of operations and support integer and floating-point numbers.

Key Requirements:

  1. Supported Operations: +, -, *, /.
  2. Operator Precedence: Multiplication and division should be evaluated before addition and subtraction.
  3. Parentheses: Expressions within parentheses should be evaluated first. Nested parentheses are allowed.
  4. Number Types: Support both integers and floating-point numbers.
  5. Whitespace: Ignore whitespace characters (spaces, tabs) within the expression string.
  6. Error Handling: The function should gracefully handle invalid expressions (e.g., syntax errors, division by zero) by raising appropriate exceptions.

Expected Behavior:

  • The function should return a numerical type (int or float) representing the evaluated result.
  • For division, standard floating-point division should be used.

Edge Cases to Consider:

  • Expressions with only numbers.
  • Expressions with only operators (invalid).
  • Expressions starting or ending with operators (invalid).
  • Division by zero.
  • Unbalanced parentheses.
  • Invalid characters in the expression.

Examples

Example 1:

Input: "3 + 5 * (2 - 8) / 2"
Output: -12.0
Explanation:
1. Parentheses first: (2 - 8) = -6
2. Expression becomes: "3 + 5 * -6 / 2"
3. Multiplication/Division (left to right):
   5 * -6 = -30
   -30 / 2 = -15.0
4. Expression becomes: "3 + -15.0"
5. Addition: 3 + -15.0 = -12.0

Example 2:

Input: "10 / 2 + 3 * (4 - 1)"
Output: 14.0
Explanation:
1. Parentheses first: (4 - 1) = 3
2. Expression becomes: "10 / 2 + 3 * 3"
3. Multiplication/Division (left to right):
   10 / 2 = 5.0
   3 * 3 = 9
4. Expression becomes: "5.0 + 9"
5. Addition: 5.0 + 9 = 14.0

Example 3:

Input: "((5 + 3) * 2) - 10"
Output: 6.0
Explanation:
1. Innermost parentheses: (5 + 3) = 8
2. Expression becomes: "(8 * 2) - 10"
3. Next parentheses: (8 * 2) = 16
4. Expression becomes: "16 - 10"
5. Subtraction: 16 - 10 = 6.0

Example 4 (Error Case):

Input: "5 / 0"
Output: Raises ZeroDivisionError
Explanation: Attempting to divide by zero.

Example 5 (Error Case):

Input: "3 + (5 - 2"
Output: Raises ValueError (or similar, indicating unbalanced parentheses)
Explanation: Mismatched parentheses.

Constraints

  • The input expression_string will be a string.
  • The expression will only contain digits (0-9), decimal points (.), operators (+, -, *, /), parentheses ((, )), and whitespace.
  • Numbers will be valid positive or negative integers or floating-point numbers.
  • The maximum nesting depth of parentheses will not exceed 100.
  • The length of the expression string will not exceed 1000 characters.
  • The evaluator should aim for reasonable performance, avoiding brute-force or overly inefficient algorithms for typical expression lengths.

Notes

  • This problem is a classic application of parsing techniques, often solved using stacks (e.g., Shunting-Yard algorithm for conversion to Reverse Polish Notation, or directly evaluating using two stacks).
  • Consider how you will handle operator precedence and associativity.
  • Thoroughly test your error handling for various invalid inputs.
  • You might find it helpful to first tokenize the expression string into a list of numbers and operators.
Loading editor...
python