Skip to content

Commit bb72389

Browse files
sozelfistvil02
andauthored
Improve Postfix Evaluation Implementation (TheAlgorithms#790)
* chore: move `postfix_evaluation.rs` from `data_structure` to `math` folder * chore: improve implementation - Add docstrings to file and function - Rewrite tests using macro. - Add some test cases * style: simplify logic by not handling empty input explicitly --------- Co-authored-by: Piotr Idzik <65706193+vil02@users.noreply.github.com>
1 parent b2acfd2 commit bb72389

File tree

4 files changed

+107
-90
lines changed

4 files changed

+107
-90
lines changed

src/data_structures/mod.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ mod hash_table;
88
mod heap;
99
mod lazy_segment_tree;
1010
mod linked_list;
11-
mod postfix_evaluation;
1211
mod probabilistic;
1312
mod queue;
1413
mod range_minimum_query;
@@ -32,7 +31,6 @@ pub use self::hash_table::HashTable;
3231
pub use self::heap::Heap;
3332
pub use self::lazy_segment_tree::LazySegmentTree;
3433
pub use self::linked_list::LinkedList;
35-
pub use self::postfix_evaluation::evaluate_postfix;
3634
pub use self::probabilistic::bloom_filter;
3735
pub use self::probabilistic::count_min_sketch;
3836
pub use self::queue::Queue;

src/data_structures/postfix_evaluation.rs

Lines changed: 0 additions & 88 deletions
This file was deleted.

src/math/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ mod perfect_cube;
5656
mod perfect_numbers;
5757
mod perfect_square;
5858
mod pollard_rho;
59+
mod postfix_evaluation;
5960
mod prime_check;
6061
mod prime_factors;
6162
mod prime_numbers;
@@ -147,6 +148,7 @@ pub use self::perfect_numbers::perfect_numbers;
147148
pub use self::perfect_square::perfect_square;
148149
pub use self::perfect_square::perfect_square_binary_search;
149150
pub use self::pollard_rho::{pollard_rho_factorize, pollard_rho_get_one_factor};
151+
pub use self::postfix_evaluation::evaluate_postfix;
150152
pub use self::prime_check::prime_check;
151153
pub use self::prime_factors::prime_factors;
152154
pub use self::prime_numbers::prime_numbers;

src/math/postfix_evaluation.rs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
//! This module provides a function to evaluate postfix (Reverse Polish Notation) expressions.
2+
//! Postfix notation is a mathematical notation in which every operator follows all of its operands.
3+
//!
4+
//! The evaluator supports the four basic arithmetic operations: addition, subtraction, multiplication, and division.
5+
//! It handles errors such as division by zero, invalid operators, insufficient operands, and invalid postfix expressions.
6+
7+
/// Enumeration of errors that can occur when evaluating a postfix expression.
8+
#[derive(Debug, PartialEq)]
9+
pub enum PostfixError {
10+
DivisionByZero,
11+
InvalidOperator,
12+
InsufficientOperands,
13+
InvalidExpression,
14+
}
15+
16+
/// Evaluates a postfix expression and returns the result or an error.
17+
///
18+
/// # Arguments
19+
///
20+
/// * `expression` - A string slice that contains the postfix expression to be evaluated.
21+
/// The tokens (numbers and operators) should be separated by whitespace.
22+
///
23+
/// # Returns
24+
///
25+
/// * `Ok(isize)` if the expression is valid and evaluates to an integer.
26+
/// * `Err(PostfixError)` if the expression is invalid or encounters errors during evaluation.
27+
///
28+
/// # Errors
29+
///
30+
/// * `PostfixError::DivisionByZero` - If a division by zero is attempted.
31+
/// * `PostfixError::InvalidOperator` - If an unknown operator is encountered.
32+
/// * `PostfixError::InsufficientOperands` - If there are not enough operands for an operator.
33+
/// * `PostfixError::InvalidExpression` - If the expression is malformed (e.g., multiple values are left on the stack).
34+
pub fn evaluate_postfix(expression: &str) -> Result<isize, PostfixError> {
35+
let mut stack: Vec<isize> = Vec::new();
36+
37+
for token in expression.split_whitespace() {
38+
if let Ok(number) = token.parse::<isize>() {
39+
// If the token is a number, push it onto the stack.
40+
stack.push(number);
41+
} else {
42+
// If the token is an operator, pop the top two values from the stack,
43+
// apply the operator, and push the result back onto the stack.
44+
if let (Some(b), Some(a)) = (stack.pop(), stack.pop()) {
45+
match token {
46+
"+" => stack.push(a + b),
47+
"-" => stack.push(a - b),
48+
"*" => stack.push(a * b),
49+
"/" => {
50+
if b == 0 {
51+
return Err(PostfixError::DivisionByZero);
52+
}
53+
stack.push(a / b);
54+
}
55+
_ => return Err(PostfixError::InvalidOperator),
56+
}
57+
} else {
58+
return Err(PostfixError::InsufficientOperands);
59+
}
60+
}
61+
}
62+
// The final result should be the only element on the stack.
63+
if stack.len() == 1 {
64+
Ok(stack[0])
65+
} else {
66+
Err(PostfixError::InvalidExpression)
67+
}
68+
}
69+
70+
#[cfg(test)]
71+
mod tests {
72+
use super::*;
73+
74+
macro_rules! postfix_tests {
75+
($($name:ident: $test_case:expr,)*) => {
76+
$(
77+
#[test]
78+
fn $name() {
79+
let (input, expected) = $test_case;
80+
assert_eq!(evaluate_postfix(input), expected);
81+
}
82+
)*
83+
}
84+
}
85+
86+
postfix_tests! {
87+
test_addition_of_two_numbers: ("2 3 +", Ok(5)),
88+
test_multiplication_and_addition: ("5 2 * 4 +", Ok(14)),
89+
test_simple_division: ("10 2 /", Ok(5)),
90+
test_operator_without_operands: ("+", Err(PostfixError::InsufficientOperands)),
91+
test_division_by_zero_error: ("5 0 /", Err(PostfixError::DivisionByZero)),
92+
test_invalid_operator_in_expression: ("2 3 #", Err(PostfixError::InvalidOperator)),
93+
test_missing_operator_for_expression: ("2 3", Err(PostfixError::InvalidExpression)),
94+
test_extra_operands_in_expression: ("2 3 4 +", Err(PostfixError::InvalidExpression)),
95+
test_empty_expression_error: ("", Err(PostfixError::InvalidExpression)),
96+
test_single_number_expression: ("42", Ok(42)),
97+
test_addition_of_negative_numbers: ("-3 -2 +", Ok(-5)),
98+
test_complex_expression_with_multiplication_and_addition: ("3 5 8 * 7 + *", Ok(141)),
99+
test_expression_with_extra_whitespace: (" 3 4 + ", Ok(7)),
100+
test_valid_then_invalid_operator: ("5 2 + 1 #", Err(PostfixError::InvalidOperator)),
101+
test_first_division_by_zero: ("5 0 / 6 0 /", Err(PostfixError::DivisionByZero)),
102+
test_complex_expression_with_multiple_operators: ("5 1 2 + 4 * + 3 -", Ok(14)),
103+
test_expression_with_only_whitespace: (" ", Err(PostfixError::InvalidExpression)),
104+
}
105+
}

0 commit comments

Comments
 (0)