Компилятор математических выражений
Автор: Глызин Дмитрий
Цели использования
В некоторых областях часто встречается ситуация, когда требуется вычислять значения функций, задаваемых непосредственно во время работы программы. Иногда для этого достаточно создания интерпретаторов, которые производят как анализ, так и вычисление выражения средствами языка высокого уровня.
Однако в случаях, когда на первое место среди качеств программы выходит ее быстродействие, такой вариант становится неприемлемым. Как раз для приложений, производящих длительные вычисления, и был создан данный модуль, анализ формулы в котором производится так же, как и в интерпретаторах (благодаря однократности действия оно не оказывает влияния на скорость), но ее расчет при конкретных значениях переменных происходит с той же (либо большей) скоростью, что и при вызове обычных функций Delphi.
Возможности
- Проверяется корректность введенного выражения.
- Вычисляются правильно составленные выражения, содержащие бинарные операции +, -, *, /, ^, любые скобки, функции sin, cos, tg, ctg, exp, ln, lg, числовые константы типа extended (с точкой в качестве десятичного разделителя), и переменные произвольной длины, состоящие из букв любого алфавита и цифр.
- Одинаковые символы в разных регистрах считаются идентичными.
- Если аргументом функции является переменная либо константа, то их не обязательно заключать в скобки.
Пример:
-(x+cosy)/Exp[z]+LN {sin пеРеменная1-tg 3.14}
|
Ограничения текущей версии
В ситуациях, когда входному выражению соответствует обратная польская запись вида:
a b c d e f g h i + + + + + + + + ,
где число подряд идущих переменных больше восьми, а также в некоторых других неудачных случаях, модуль откажется генерировать код функции.
Это связано с использованием при вычислениях одного лишь стека сопроцессора для хранения промежуточных результатов, и будет исправлено в следующей версии.
Кроме того, для нормальной работы придется отключить Tools/Debugger options/Language Exceptions/Stop on Delphi Exceptions, иначе будет довольно утомительно: при анализе исключения возникают десятками.
Суть действий модуля
В памяти, соответствующей переменной типа "массив байтов", создается машинный код, соответствующий входной строке, после чего переменной типа "function:extended" присваивается адрес начала массива.
Использование модуля
1. Заводим переменную типа TFormula:
TFormula = record
CS: CodeSeg; // массив с кодом
DS: DataSeg; // массив с переменными и константами
proc: tproc; // вызываемая функция
end;
var
formula1: tformula;
|
2. Вызываем процедуру компиляции кода, в которой указываем нашу formula1, список имен используемых в ней переменных, и, конечно, входную строку.
3. Для вычисления значения функции в formula1.DS записываем значения переменных в том порядке, в котором их имена фигурировали в списке (при этом важно изменять только первые 0..число переменных-1 элементы DS, т.к. в последующих элементах хранятся значения констант из входной строки), а затем вызываем formula1.proc, которая и возвратит искомое значение.
Информация более конкретного характера содержится в самом модуле.
Использованные идеи и алгоритмы
- Алгоритм Эрли проверки корректности входной строки.
- Упрощенный вариант алгоритма Дейкстры перевода в обратную польскую запись на основе стека с приоритетами.
- Способ формирования кода в памяти из программы Сергея Втюрина.
- Методы вычисления различных математических функций из открытых исходников модуля Math.
Скачать проект Compiler.zip (25K)
|