PHP로 계산기를 만드는 방법?
PHPPHP로 계산기를 만드는 방법?
PHP를 사용하여 일반 사용자가 태그를 통해 입력 한 8 * (5 + 1)과 같은 간단한 대수 표현식을 계산하려고합니다. 즉, 일반 표기법 : Multiply (8, Add (5, 1))). 또한 모든 단계를 보여 주어야하지만 어렵지 않습니다. 문제는 바로 지금 표현의 가치를 계산하는 것입니다.
참고 : 이것은 지금까지 생각한 것입니다. 매우 비효율적이지만 단호한 솔루션입니다. 가능한 경우 문자열을 교체하십시오. 예에서 문자열 5 + 1을 인식하고 6으로 바꾸십시오. 그런 다음 다시 반복하고 6을 6으로 바꾸고 다시 반복하고 8 * 6을 48로 대체하십시오. 예를 들어, 곱하기 코드는 다음과 같아야합니다.
for ($a=1; $a < 1000; $a++) {
for ($b=1; $b < 1000; $b++) {
string_replace($a . '*' . $b, $a*$b, $string);
}
}
해결법
-
==============================
1.필요에 따라 Shunting Yard Algorithm을 살펴 보는 것이 좋습니다. 구현하기가 쉽고 꽤 잘 작동합니다.
필요에 따라 Shunting Yard Algorithm을 살펴 보는 것이 좋습니다. 구현하기가 쉽고 꽤 잘 작동합니다.
나는 잠시 전에 채찍질 한 예를 보았습니다 : GIST.
코드 복사 / 붙여 넣기는 다음과 같습니다.
표현식 정의 :
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); } }
마지막으로 executor 클래스는 다음과 같습니다.
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 (Reverse Polish Notation) 스택으로 변환합니다. 그런 다음 스택을 실행하는 것만 큼 문제가됩니다. 다음은 간단한 예입니다.
$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을 사용하지 않고 변수를 지원하는 실행중인 알고리즘의보기 좋은 예입니다 ...
-
==============================
2.관심있을 수도있는 bcParserPHP라는 수학 파서 클래스가 있습니다.
관심있을 수도있는 bcParserPHP라는 수학 파서 클래스가 있습니다.
상당히 사용하기 쉽고 꽤 강력합니다.
해당 사이트의 예제 코드 :
$parser = new MathParser(); $parser->setVariable('X', 5); $parser->setVariable('Y', 2); $parser->setExpression('COS(X)+SIN(Y)/2'); echo $parser->getValue();
불행히도, 그것은 상용 제품입니다; 나는 그것이 당신을 멈추게 할 것인가 아닌가를 모른다면 (그것이 가격과 당신의 필요에 달려 있다고 생각하는 것).
비상업적 인 대안이 될 수 있습니다 : http://www.phpclasses.org/package/2695-PHP-Safely-evaluate-mathematical-expressions.html
이 클래스는 내부적으로 eval ()을 사용합니다. 가능한 경우 그렇게하지 않는 것이 좋습니다.
그렇게하지 않으면 자신의 언어 구문 분석기를 작성하는 것이 이상적인 솔루션이지만 PHP에서는 그렇게하는 것이 바람직하지 않습니다.
-
==============================
3.나는 표현식에 있어서는 안되는 것들의 입력을 제거함으로써 시작할 것입니다 (여러분이 단지 덧셈, 뺄셈, 곱셈, 나눗셈 및 변수 없음을 허용한다고 가정 할 때).
나는 표현식에 있어서는 안되는 것들의 입력을 제거함으로써 시작할 것입니다 (여러분이 단지 덧셈, 뺄셈, 곱셈, 나눗셈 및 변수 없음을 허용한다고 가정 할 때).
$expr = preg_replace('/[^0-9+*\/-]/', '', $expr);
그런 다음 사용자 입력에 위험한 것이 아무것도 없다고 확신하면 eval ()을 사용하여 표현식을 평가하면됩니다.
$result = eval("return $expr;");
바퀴를 재발 명할 필요가 없습니다.
Kolink의 수정 사항을 통합하여 편집했습니다. 감사!
from https://stackoverflow.com/questions/12692727/how-to-make-a-calculator-in-php by cc-by-sa and MIT license
'PHP' 카테고리의 다른 글
구성 파일을 직접 액세스에서 숨기는 방법? (0) | 2018.09.10 |
---|---|
경고 : mysqli_error ()는 정확히 1 개의 매개 변수를 요구하고, 0은 오류를 제공한다 [닫힘] (0) | 2018.09.10 |
UTF-8 문자열을 사용하여 PHP에서 파일 시스템 함수를 사용하려면 어떻게해야합니까? (0) | 2018.09.10 |
PHP에서 함수가 반환 한 액세스 배열 (0) | 2018.09.10 |
LIKE 문을 사용하여 PDO 매개 변수가있는 쿼리를 만드는 방법은 무엇입니까? (0) | 2018.09.10 |