如何用PHP制作计算器?

我想使用PHP来计算普通用户8*(5+1)通过<input>标记输入的简单代数表达式,例如,(这意味着

:不会像那样更改语法Multiply(8, Add(5, 1)))。此外,它必须显示所有步骤,但这并不难。问题是,现在,正在计算表达式的值。

注意:这是我到目前为止的想法,虽然效率很低,但这是临时的解决方案。只要可能就替换字符串:在我们的示例中,识别该字符串5+1并将其替换为6。然后,再次循环,替换(6)6,再次循环,然后替换8*648。例如,用于乘法的代码应如下所示:

for ($a=1; $a < 1000; $a++) {

for ($b=1; $b < 1000; $b++) {

string_replace($a . '*' . $b, $a*$b, $string);

}

}

回答:

根据您的需求,建议您使用Shunting Yard Algorithm。它很容易实现,并且效果很好。

这是将代码复制/粘贴到一个块中的代码:

表达式定义:

class Parenthesis extends TerminalExpression {

protected $precidence = 7;

public function operate(Stack $stack) {

}

public function getPrecidence() {

return $this->precidence;

}

public function isNoOp() {

return true;

}

public function isParenthesis() {

return true;

}

public function isOpen() {

return $this->value == '(';

}

}

class Number extends TerminalExpression {

public function operate(Stack $stack) {

return $this->value;

}

}

abstract class Operator extends TerminalExpression {

protected $precidence = 0;

protected $leftAssoc = true;

public function getPrecidence() {

return $this->precidence;

}

public function isLeftAssoc() {

return $this->leftAssoc;

}

public function isOperator() {

return true;

}

}

class Addition extends Operator {

protected $precidence = 4;

public function operate(Stack $stack) {

return $stack->pop()->operate($stack) + $stack->pop()->operate($stack);

}

}

class Subtraction extends Operator {

protected $precidence = 4;

public function operate(Stack $stack) {

$left = $stack->pop()->operate($stack);

$right = $stack->pop()->operate($stack);

return $right - $left;

}

}

class Multiplication extends Operator {

protected $precidence = 5;

public function operate(Stack $stack) {

return $stack->pop()->operate($stack) * $stack->pop()->operate($stack);

}

}

class Division extends Operator {

protected $precidence = 5;

public function operate(Stack $stack) {

$left = $stack->pop()->operate($stack);

$right = $stack->pop()->operate($stack);

return $right / $left;

}

}

class Power extends Operator {

protected $precidence=6;

public function operate(Stack $stack) {

$left = $stack->pop()->operate($stack);

$right = $stack->pop()->operate($stack);

return pow($right, $left);

}

}

abstract class TerminalExpression {

protected $value = '';

public function __construct($value) {

$this->value = $value;

}

public static function factory($value) {

if (is_object($value) && $value instanceof TerminalExpression) {

return $value;

} elseif (is_numeric($value)) {

return new Number($value);

} elseif ($value == '+') {

return new Addition($value);

} elseif ($value == '-') {

return new Subtraction($value);

} elseif ($value == '*') {

return new Multiplication($value);

} elseif ($value == '/') {

return new Division($value);

} elseif ($value == '^') {

return new Power($value);

} elseif (in_array($value, array('(', ')'))) {

return new Parenthesis($value);

}

throw new Exception('Undefined Value ' . $value);

}

abstract public function operate(Stack $stack);

public function isOperator() {

return false;

}

public function isParenthesis() {

return false;

}

public function isNoOp() {

return false;

}

public function render() {

return $this->value;

}

}

堆栈(非常简单的实现):

class Stack {

protected $data = array();

public function push($element) {

$this->data[] = $element;

}

public function poke() {

return end($this->data);

}

public function pop() {

return array_pop($this->data);

}

}

最后,执行者类:

class Math {

protected $variables = array();

public function evaluate($string) {

$stack = $this->parse($string);

return $this->run($stack);

}

public function parse($string) {

$tokens = $this->tokenize($string);

$output = new Stack();

$operators = new Stack();

foreach ($tokens as $token) {

$token = $this->extractVariables($token);

$expression = TerminalExpression::factory($token);

if ($expression->isOperator()) {

$this->parseOperator($expression, $output, $operators);

} elseif ($expression->isParenthesis()) {

$this->parseParenthesis($expression, $output, $operators);

} else {

$output->push($expression);

}

}

while (($op = $operators->pop())) {

if ($op->isParenthesis()) {

throw new RuntimeException('Mismatched Parenthesis');

}

$output->push($op);

}

return $output;

}

public function registerVariable($name, $value) {

$this->variables[$name] = $value;

}

public function run(Stack $stack) {

while (($operator = $stack->pop()) && $operator->isOperator()) {

$value = $operator->operate($stack);

if (!is_null($value)) {

$stack->push(TerminalExpression::factory($value));

}

}

return $operator ? $operator->render() : $this->render($stack);

}

protected function extractVariables($token) {

if ($token[0] == '$') {

$key = substr($token, 1);

return isset($this->variables[$key]) ? $this->variables[$key] : 0;

}

return $token;

}

protected function render(Stack $stack) {

$output = '';

while (($el = $stack->pop())) {

$output .= $el->render();

}

if ($output) {

return $output;

}

throw new RuntimeException('Could not render output');

}

protected function parseParenthesis(TerminalExpression $expression, Stack $output, Stack $operators) {

if ($expression->isOpen()) {

$operators->push($expression);

} else {

$clean = false;

while (($end = $operators->pop())) {

if ($end->isParenthesis()) {

$clean = true;

break;

} else {

$output->push($end);

}

}

if (!$clean) {

throw new RuntimeException('Mismatched Parenthesis');

}

}

}

protected function parseOperator(TerminalExpression $expression, Stack $output, Stack $operators) {

$end = $operators->poke();

if (!$end) {

$operators->push($expression);

} elseif ($end->isOperator()) {

do {

if ($expression->isLeftAssoc() && $expression->getPrecidence() <= $end->getPrecidence()) {

$output->push($operators->pop());

} elseif (!$expression->isLeftAssoc() && $expression->getPrecidence() < $end->getPrecidence()) {

$output->push($operators->pop());

} else {

break;

}

} while (($end = $operators->poke()) && $end->isOperator());

$operators->push($expression);

} else {

$operators->push($expression);

}

}

protected function tokenize($string) {

$parts = preg_split('((\d+|\+|-|\(|\)|\*|/)|\s+)', $string, null, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);

$parts = array_map('trim', $parts);

return $parts;

}

}

它通过首先标记输入(基于单词边界和标记)来工作。然后,它在其上运行Shunting

Yard算法,以将输入转换为RPN(反向波兰表示法)堆栈。然后,只需执行堆栈即可。这是一个简单的示例:

$math = new Math();

$answer = $math->evaluate('(2 + 3) * 4');

var_dump($answer);

// int(20)

$answer = $math->evaluate('1 + 2 * ((3 + 4) * 5 + 6)');

var_dump($answer);

// int(83)

$answer = $math->evaluate('(1 + 2) * (3 + 4) * (5 + 6)');

var_dump($answer);

// int(231)

$math->registerVariable('a', 4);

$answer = $math->evaluate('($a + 3) * 4');

var_dump($answer);

// int(28)

$math->registerVariable('a', 5);

$answer = $math->evaluate('($a + $a) * 4');

var_dump($answer);

// int(40)

现在,此示例比您可能需要的复杂得多。原因是它还处理分组和运算符优先级。但这是运行算法的一个很好的例子,它不使用EVAL并支持变量…

以上是 如何用PHP制作计算器? 的全部内容, 来源链接: utcz.com/qa/418141.html

回到顶部