Re[2]: вычислить выражение записанное в виде строки символов
От: system.console  
Дата: 01.02.26 10:26
Оценка:
M>Опиши, какие операторы будешь использовать, и их приоритет. Напиши, что хочешь использовать группирующие скобки. Скорми это дипсику, и попроси его написать парсер рекурсивного спуска. Он справится. Я так делал. Единственно, у меня уже был лексер, и на выходе лексера (на входе парсера) уже были токены — оператор/литерал/идентификатор/скобка. Он мне на питоне написал, но я без проблем переделал под себя на плюсиках. Возможно, на PHP он сразу сможет написать. Ну, и пусть тебе лексер напишет, или совместит его с парсером.
M>Да, не забудь указать, что +/- могут быть унарными.
M>Скажи ему, что значения хочешь складывать на стек, вызывать операторную функцию, и помещать результат обратно на стек.
я попробую конечно чисто заради любопытства посмотреть, на что он способен этот ваш гигачат, но мне хотелось бы что-то совсем коротко чтобы.
Может какую-то библиотечку подключить, где есть нужная мне функция.
А то все эти грамматики-парсеры-лексеры я уже давно забыл, если и знал когда-то.
На худой конец оставлю eval, только предварительно проверку сделаю, чтобы там в качестве параметра ничего кроме 0..9,+,-,*,/,(,) не могло пролезть

ЗЫ. Ниже результат работы gigachat

// Функция для обработки арифметического выражения
function calculateExpression($expression)
{
    // Очищаем пробелы
    $expression = str_replace(' ', '', $expression);
    
    try {
        // Используем встроенную функцию eval() осторожно
        return eval("return ($expression);");
    } catch (\\ParseError | \\DivisionByZeroError $e) {
        throw new Exception("Ошибка вычисления выражения: {$e->getMessage()}");
    }
}


или без использования eval()
class Calculator {
    private function isOperator($char) {
        return in_array($char, ['+', '-', '*', '/']);
    }

    private function precedence($op) {
        if ($op === '+' || $op === '-') return 1;
        if ($op === '*' || $op === '/') return 2;
        return 0;
    }

    private function applyOperation(&$stackValues, &$stackOperators) {
        $right = array_pop($stackValues);
        $left = array_pop($stackValues);
        $operator = array_pop($stackOperators);
        
        switch ($operator) {
            case '+':
                array_push($stackValues, $left + $right);
                break;
            case '-':
                array_push($stackValues, $left - $right);
                break;
            case '*':
                array_push($stackValues, $left * $right);
                break;
            case '/':
                if ($right != 0) {
                    array_push($stackValues, $left / $right);
                } else {
                    throw new Exception("Деление на ноль!");
                }
                break;
        }
    }

    public function evaluate($expression) {
        $tokens = preg_split('/(\\d+(?:\\.\\d+)?|\\(|\\)|[+\\-*\\/])/', trim($expression), -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
        $stackValues = [];
        $stackOperators = [];

        foreach ($tokens as $token) {
            if (is_numeric($token)) { // Число
                array_push($stackValues, floatval($token));
            } elseif ($this->isOperator($token)) { // Операция
                while (!empty($stackOperators) && $this->precedence(end($stackOperators)) >= $this->precedence($token)) {
                    $this->applyOperation($stackValues, $stackOperators);
                }
                array_push($stackOperators, $token);
            } elseif ($token === '(') { // Открывающая скобка
                array_push($stackOperators, $token);
            } elseif ($token === ')') { // Закрывающая скобка
                while (end($stackOperators) !== '(') {
                    $this->applyOperation($stackValues, $stackOperators);
                }
                array_pop($stackOperators); // Убираем открывающую скобку
            }
        }

        while (!empty($stackOperators)) {
            $this->applyOperation($stackValues, $stackOperators);
        }

        return end($stackValues);
    }
}
Отредактировано 01.02.2026 11:45 system.console . Предыдущая версия . Еще …
Отредактировано 01.02.2026 11:37 system.console . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.