Last active
July 9, 2025 13:16
-
-
Save theking2/0f3184ba8f4a9fd604d8e9cfeb64fbc9 to your computer and use it in GitHub Desktop.
Polish notation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Polish notation | |
* @param $expression - Space seperated operators and param indices | |
* @param $param - two or more params for the calculation | |
*/ | |
function pn(string $expression, mixed ...$param) { | |
$operators = "+-*/%"; $delimiter = " "; | |
$expression = explode($delimiter, $expression); | |
$stack = []; | |
do { | |
$itm=array_pop($expression); | |
if( !strstr($operators, $itm) ) { // treat everything not an operator as operand | |
if( !ctype_digit( $itm ) ) { | |
throw new \InvalidArgumentException( "Operand index not integer: $itm\n" ); | |
} | |
if(! (0 <= $itm and $itm < count( $param )) ) { | |
throw new \OutOfBoundsException ( "Operand index out of range: $itm\n" ); | |
} | |
if( !is_numeric( $param[$itm] ) ) { | |
throw new \InvalidArgumentException( "Operand not numeric: $param[$itm]\n" ); | |
} | |
array_push($stack, $param[$itm]); | |
continue; | |
} | |
// we have an operator | |
switch($itm) { | |
case '+': array_push( $stack, array_pop($stack) + array_pop($stack) ); break; | |
case '-': array_push( $stack, array_pop($stack) - array_pop($stack) ); break; | |
case '*': array_push( $stack, array_pop($stack) * array_pop($stack) ); break; | |
case '/': array_push( $stack, array_pop($stack) / array_pop($stack) ); break; | |
case '%': array_push( $stack, array_pop($stack) % array_pop($stack) ); break; | |
//default: throw new \InvalidArgumentException( "Unknown operator $itm" ); | |
} | |
} while ($expression); | |
return array_pop($stack); | |
} | |
// Parameters are indexed from 0 upwards | |
try{ | |
echo pn("/ + 3 0 / 2 1", 30, 40, 50, 69), "\n"; | |
} catch( Exception $e) {echo $e->getMessage();} | |
try{ | |
echo pn("/ + 3 0 / 2 1", 30, 40, 50), "\n"; | |
} catch( Exception $e) {echo $e->getMessage();} | |
try{ | |
echo pn("/ + 3 3 / 2 1.1", 30, 40, 50, "a"), "\n"; | |
} catch( Exception $e) {echo $e->getMessage();} | |
try{ | |
echo pn("/ + 3 3 / 2 1", 30, 40, 50, "a"), "\n"; | |
} catch( Exception $e) {echo $e->getMessage();} | |
// If parameters are in an array already use unpacking | |
$operants = [3, 4, 5, 6]; | |
$expresion = "/ + 0 1 / 2 3"; | |
echo pn($expresion, ...$operants), "\n"; | |
/** | |
* Evaluates a prefix (Polish Notation) expression. | |
* | |
* Operands in the expression string are expected to be integer indices | |
* referring to values in the $param variable-length argument list. | |
* | |
* @param $expression The prefix expression, with elements separated by a space. | |
* @param $param A variable number of parameters, which serve as the actual operands. | |
* @return The result of the prefix expression. | |
* @throws \InvalidArgumentException If an operand index is not an integer, | |
* an actual operand is not numeric, | |
* not enough operands for an operator, | |
* an unknown operator is encountered, | |
* or the expression does not resolve to a single result. | |
* @throws \OutOfBoundsException If an operand index is out of the range of the $param array. | |
* @throws \DivisionByZeroError If a division or modulo by zero occurs. | |
*/ | |
function ppn(string $expression, mixed ...$param): int|float { | |
$operators = "+-*/%"; | |
$delimiter = " "; | |
$expression_tokens = explode($delimiter, $expression); | |
$stack = []; | |
// Process tokens from right to left using array_pop. | |
// This is the standard way to evaluate prefix expressions. | |
while (!empty($expression_tokens)) { | |
$token = array_pop($expression_tokens); | |
// Check if the current token is an operator. | |
if (str_contains($operators, $token)) { | |
// Operator: Pop operands, perform operation, push result. | |
// Ensure there are enough operands on the stack. | |
if (count($stack) < 2) { | |
throw new \InvalidArgumentException("Not enough operands for operator '{$token}' in prefix expression."); | |
} | |
// For prefix notation evaluated right-to-left: | |
// The first popped operand is the "right" operand. | |
// The second popped operand is the "left" operand. | |
$operand1 = array_pop($stack); // Right operand | |
$operand2 = array_pop($stack); // Left operand | |
$result = 0; // Initialize result variable | |
switch ($token) { | |
case '+': | |
$result = $operand2 + $operand1; | |
break; | |
case '-': | |
$result = $operand2 - $operand1; | |
break; | |
case '*': | |
$result = $operand2 * $operand1; | |
break; | |
case '/': | |
if ($operand1 == 0) { // Check for division by zero (right operand is the divisor) | |
throw new \DivisionByZeroError("Division by zero in prefix expression."); | |
} | |
$result = $operand2 / $operand1; | |
break; | |
case '%': | |
if ($operand1 == 0) { // Check for modulo by zero (right operand is the divisor) | |
throw new \DivisionByZeroError("Modulo by zero in prefix expression."); | |
} | |
$result = $operand2 % $operand1; | |
break; | |
default: | |
// This should theoretically not be reached if $operators is exhaustive | |
// and validation for operands passes first. | |
throw new \InvalidArgumentException("Unknown operator '{$token}' encountered in prefix expression."); | |
} | |
array_push($stack, $result); | |
} else { | |
// Operand: Validate the index and the parameter value, then push to stack. | |
if (!ctype_digit($token)) { | |
throw new \InvalidArgumentException("Operand index not an integer: {$token}"); | |
} | |
$index = (int)$token; // Convert string token to integer index | |
if (! (0 <= $index && $index < count($param))) { | |
throw new \OutOfBoundsException("Operand index out of range: {$index}"); | |
} | |
if (!is_numeric($param[$index])) { | |
throw new \InvalidArgumentException("Operand at index {$index} not numeric: {$param[$index]}"); | |
} | |
array_push($stack, $param[$index]); | |
} | |
} | |
// After processing all tokens, the stack should contain only the final result. | |
if (count($stack) !== 1) { | |
throw new \InvalidArgumentException("Invalid prefix expression: Stack did not resolve to a single result."); | |
} | |
return array_pop($stack); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
FSR
Make this into a class to
' '