복붙노트

PHP로 계산기를 만드는 방법?

PHP

PHP로 계산기를 만드는 방법?

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. ==============================

    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. ==============================

    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. ==============================

    3.나는 표현식에 있어서는 안되는 것들의 입력을 제거함으로써 시작할 것입니다 (여러분이 단지 덧셈, 뺄셈, 곱셈, 나눗셈 및 변수 없음을 허용한다고 가정 할 때).

    나는 표현식에 있어서는 안되는 것들의 입력을 제거함으로써 시작할 것입니다 (여러분이 단지 덧셈, 뺄셈, 곱셈, 나눗셈 및 변수 없음을 허용한다고 가정 할 때).

     $expr = preg_replace('/[^0-9+*\/-]/', '', $expr);
    

    그런 다음 사용자 입력에 위험한 것이 아무것도 없다고 확신하면 eval ()을 사용하여 표현식을 평가하면됩니다.

     $result = eval("return $expr;");
    

    바퀴를 재발 명할 필요가 없습니다.

    Kolink의 수정 사항을 통합하여 편집했습니다. 감사!

  4. from https://stackoverflow.com/questions/12692727/how-to-make-a-calculator-in-php by cc-by-sa and MIT license