PCSpectra wrote:Try and implement one that supports infix and you'll see what they mean.

Thank you PCSpectra. Implementing that feature caused me to refactor the number class.
I have changed the following:
- The number class now has only 1 get and set method. The base is specified as an optional second parameter.
- I removed the base_convert() function because it doesn't support floats. I replaced it with other base conversion functions, but they don't seem to be reliable either

arborint wrote:Perhaps you could implement an op($op, $value) method in your class that is called like $calc->op('/', 2) or $calc->op('+', 10).
I think that's a good idea, but when I tried to do it, it seemed that it would be a bit clunky. I'll look into it more.
Here are the revised classes:
Code: Select all
class calculator {
private $_n;
public function __construct(number $n) {
$this->_n = $n;
}
public function add(number $n) {
$this->_n->set($this->_n->get() + $n->get());
}
public function subtract(number $n) {
$this->_n->set($this->_n->get() - $n->get());
}
public function divide(number $n) {
$this->_n->set($this->_n->get() / $n->get());
}
public function multiply(number $n) {
$this->_n->set($this->_n->get() * $n->get());
}
public function equals() {
return $this->_n;
}
}
class number {
private $_n;
public function __construct($n = 0, $base = 10) {
$this->_store($n, $base);
}
public function set($n, $base = 10) {
$this->_store($n, $base);
}
public function get($base = 10) {
return $this->_retrieve($base);
}
private function _store($n, $base) {
switch ($base) {
case 2:
$this->_n = bindec($n);
break;
case 8:
$this->_n = octdec($n);
break;
case 10:
$this->_n = $n;
break;
case 16:
$this->_n = dexhec($n);
break;
default:
$this->_n = $n;
trigger_error("" . get_class($this) . "() Second parameter must be one of 2, 8, 10, 16");
break;
}
}
private function _retrieve($base) {
switch ($base) {
case 2:
return decbin($this->_n);
break;
case 8:
return decoct($this->_n);
break;
case 10:
return $this->_n;
break;
case 16:
return hexdec($this->_n);
break;
default:
trigger_error("" . get_class($this) . "() Second parameter must be one of 2, 8, 10, 16");
return $this->_n;
break;
}
}
}
class numberSet {
private $_numbers = array();
public function addNumber(number $number) {
$this->_numbers[] = $number;
}
public function getAverage() {
$calculator = new calculator(new number());
foreach ($this->_numbers as $number) {
$calculator->add($number);
}
$calculator->divide(new number(count($this->_numbers)));
return $calculator->equals();
}
}
Here is infix notation parser:
Code: Select all
/*
* NOTE: does not support exponents or roots
*/
class infixNotation {
/*
* stores the original equation
*/
private $_equation;
/*
* stores the equation as it is calculated by the parsing engine
*/
private $_equation_buffer;
/*
* stores the number base of the equation
*/
private $_base;
/*
* regex for finding the inner most expression inside ()
*/
const REGEX_NESTED_EXPRESSION = '#\(([^()]{1,})\)#';
/*
* regex for finding the first multiplication or division set in an expression
*/
const REGEX_FIRST_MUL_OR_DIV = '#([\da-f\.]+)\s*([/\*])\s*([\da-f\.]+)#';
/*
* regex for finding the first subtraction or addition set in an expression
*/
const REGEX_FIRST_SUB_OR_ADD = '#([\da-f\.]+)\s*([-\+])\s*([\da-f\.]+)#';
public function __construct($e = '', $base = 10) {
$this->setEquation($e);
$this->_base = $base;
}
public function setEquation($e, $base = 10) {
$this->_equation = $e;
$this->_base = $base;
}
public function getResult() {
$this->_equation_buffer = $this->_equation;
while ($expression = $this->_parseEquation()) {
$this->_equation_buffer = preg_replace(self::REGEX_NESTED_EXPRESSION, $this->_parseExpression($expression), $this->_equation_buffer, 1);
}
return $this->_equation_buffer = $this->_parseExpression($this->_equation_buffer);
}
private function _parseEquation() {
if (preg_match(self::REGEX_NESTED_EXPRESSION, $this->_equation_buffer, $expression)) {
return $expression[1];
} else {
return false;
}
}
private function _parseExpression($expression) {
while (preg_match(self::REGEX_FIRST_MUL_OR_DIV, $expression, $x)) {
$calculate = new calculator(new number($x[1], $this->_base));
if ($x[2] == '/') {
$calculate->divide(new number($x[3], $this->_base));
} else {
$calculate->multiply(new number($x[3], $this->_base));
}
$result = $calculate->equals();
$expression = preg_replace(self::REGEX_FIRST_MUL_OR_DIV, $result->get($this->_base), $expression, 1);
}
while (preg_match(self::REGEX_FIRST_SUB_OR_ADD, $expression, $x)) {
$calculate = new calculator(new number($x[1], $this->_base));
if ($x[2] == '-') {
$calculate->subtract(new number($x[3], $this->_base));
} else {
$calculate->add(new number($x[3], $this->_base));
}
$result = $calculate->equals();
$expression = preg_replace(self::REGEX_FIRST_SUB_OR_ADD, $result->get($this->_base), $expression, 1);
}
return trim($expression);
}
}
Here is a base 10 use case for the notation parser:
Code: Select all
$inf = new infixNotation('2+2');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('2 + 2');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('2 + 2 * 10');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 + 2) * 10');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 + 2) * 10 / 5');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 + 2) * 10 / 5 + 50');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 + 2) * 10 / 5 + 50 * 500');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 + 2) * 10 / (5 + 50) * 500');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 + 2) + (10 / 5) + 50 * 500 - 100');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 * 2 + 3 * 10) + (10 / 5) + 50 * 500 - 100');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 * (2 + 3) * 10) + (10 / 5) + 50 * 500 - 100');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 * (2 * 5 + 3) * 10) + (10 / 5) + 50 * 500 - 100');
echo $inf->getResult() . "<br>\n";
$inf = new infixNotation('(2 * (2 * (5 + 3)) * 10) + (10 / 5) + 50 * 500 - 100');
echo $inf->getResult() . "<br>\n";
And the output:
4
4
22
40
8
58
25008
363.636363636
24906
24936
25002
25162
25222
Here is a base 2 (binary) use case for the notation parser:
Code: Select all
$inf = new infixNotation('10+10', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('10 + 10', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('10 + 10 * 1010', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 + 10) * 1010', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 + 10) * 1010 / 101', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 + 10) * 1010 / 101 + 110010', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 + 10) * 1010 / 101 + 110010 * 111110100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 + 10) * 1010 / (101 + 110010) * 111110100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 + 10) + (1010 / 101) + 110010 * 111110100 - 1100100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 * 10 + 11 * 1010) + (1010 / 101) + 110010 * 111110100 - 1100100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 * (10 + 11) * 1010) + (1010 / 101) + 110010 * 111110100 - 1100100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 * (10 * 101 + 11) * 1010) + (1010 / 101) + 110010 * 111110100 - 1100100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
$inf = new infixNotation('(10 * (10 * (101 + 11)) * 1010) + (1010 / 101) + 110010 * 111110100 - 1100100', 2);
$x = new number($inf->getResult(), 2);
echo $x->get() . "\n<br />";
And the output:
4
4
22
40
8
58
25008
0
24906
24936
25002
25162
25222
As you can see, the numbers class is having issues base converting floats
Other than that I'm pretty happy with how this has progressed. What's next?