Каков самый умный способ разработки математического анализатора? Я имею в виду функцию, которая принимает строку математики (например: "2 + 3/2 + (2 * 5)" ) и возвращает вычисленное значение? Я писал один в VB6 давным-давно, но в итоге он стал раздутым и не очень портативным (или умным в этом отношении...). Приветствуются общие идеи, код psuedo или реальный код.
Умный дизайн математического анализатора?
Ответ 1
Довольно хороший подход предполагает два шага. Первый шаг включает преобразование выражения из infix в postfix (например, через Dijkstra shunting двор) обозначение. После этого достаточно тривиально написать постфиксный оценщик.
Ответ 2
Я написал несколько сообщений в блогах о разработке математического анализатора. Существует общий introduction, базовые знания о grammars, примерная реализация, написанная на Ruby, и набор тестов. Возможно, вы найдете эти материалы полезными.
Ответ 3
У вас есть несколько подходов. Вы можете создать динамический код и выполнить его, чтобы получить ответ, не требуя много писать код. Просто выполните поиск по исполняемому сценарию в .NET, и есть много примеров.
В качестве альтернативы вы можете создать фактический парсер и создать небольшое дерево разбора, которое затем используется для оценки выражения. Опять же, это довольно просто для базовых выражений. Проверьте codeplex, поскольку я считаю, что у них есть математический парсер. Или просто посмотрите BNF, который будет содержать примеры. Любой веб-сайт, представляющий концепции компилятора, будет включать это в качестве базового примера.
Ответ 4
Я знаю, что это старо, но я столкнулся с этим, пытаясь разработать калькулятор как часть более крупного приложения и столкнулся с некоторыми проблемами, используя принятый ответ. Ссылки были IMMENSELY полезны в понимании и решении этой проблемы и не должны быть снижены. Я писал приложение для Android на Java и для каждого элемента в выражении "строка" я фактически сохранил String в ArrayList, когда пользователь набирает на клавиатуре. Для преобразования infix-to-postfix я повторял каждую строку в ArrayList, а затем оценивал недавно созданный постфикс ArrayList строк. Это было фантастически для небольшого числа операндов/операторов, но более длинные вычисления были последовательно отключены, особенно когда выражения начали оценивать нецелые числа. В приведенной ссылке для Infix to Postfix conversion" предлагается всплывать стек, если отсканированный элемент является оператором, а элемент topStack имеет более высокий приоритет. Я обнаружил, что это почти правильно. Высказывание элемента topStack, если приоритет выше, или EQUAL для отсканированного оператора, наконец, сделали мои вычисления правильными. Надеюсь, это поможет любому, кто работает над этой проблемой, и благодаря Джастину Полию (и fas?) За предоставление некоторых бесценных ссылок.
Ответ 5
Если у вас есть приложение "всегда включено", просто отправьте строку математики в Google и проанализируйте результат. Простой способ, но не уверен, что это то, что вам нужно, - но умный, каким-то образом, я думаю.
Ответ 6
Связанный с нами вопрос анализатор (выражение) с приоритетом? содержит некоторую полезную информацию о том, как начать с этого.
-Adam
Ответ 7
Предполагая, что ваш ввод является выражением инфикса в строчном формате, вы можете преобразовать его в postfix и, используя пару стеков: оператор стека и стека операндов, откройте решение. Вы можете найти общую информацию об алгоритме по ссылке в Википедии.
Ответ 8
ANTLR - очень хороший генератор парсеров LL (*). Я рекомендую его очень.
Ответ 9
Разработчики всегда хотят иметь чистый подход и попытаться реализовать логику синтаксического анализа с нуля, обычно заканчивая Dijkstra Shunting-Yard Algorithm. Результат - аккуратный код, но, возможно, с ошибками. Я разработал такой API, JMEP, который делает все это, но мне потребовались годы, чтобы иметь стабильный код.
Даже со всей этой работой вы можете видеть даже на этой странице проекта, которую я серьезно рассматриваю, чтобы перейти на использование JavaCC или ANTLR, даже после того, как все это уже сделано.
Ответ 10
Прошло 11 лет с того момента, когда был задан этот вопрос: если вы не хотите заново изобретать колесо, есть много экзотических математических парсеров.
Я написал один год назад, который поддерживает арифметические операции, решение уравнений, дифференциальное исчисление, интегральное исчисление, базовую статистику, определение функции/формулы, построение графиков и т.д.
Его называют ParserNG и его бесплатно.
Оценить выражение так же просто, как:
MathExpression expr = new MathExpression("(34+32)-44/(8+9(3+2))-22");
System.out.println("result: " + expr.solve());
result: 43.16981132075472
Или используя переменные и вычисляя простые выражения:
MathExpression expr = new MathExpression("r=3;P=2*pi*r;");
System.out.println("result: " + expr.getValue("P"));
Или используя функции:
MathExpression expr = new MathExpression("f(x)=39*sin(x^2)+x^3*cos(x);f(3)");
System.out.println("result: " + expr.solve());
result: -10.65717648378352
Или оценить производную в заданной точке (обратите внимание, что она выполняет символическое дифференцирование (не числовое) за кулисами, поэтому точность не ограничивается ошибками числовых приближений):
MathExpression expr = new MathExpression("f(x)=x^3*ln(x); diff(f,3,1)");
System.out.println("result: " + expr.solve());
result: 38.66253179403897
Что дифференцирует x^3 * ln(x)
один раз при x = 3. Количество раз, которое вы можете различить, составляет 1 на данный момент.
или для числовой интеграции:
MathExpression expr = new MathExpression("f(x)=2*x; intg(f,1,3)");
System.out.println("result: " + expr.solve());
result: 7.999999999998261... approx: 8
Этот парсер работает довольно быстро и обладает множеством других функций.
Завершена работа по переносу его в Swift через привязки к Objective C, и мы использовали его в графических приложениях среди других итеративных сценариев использования.
ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: ParserNG создан мной.