Rambler's Top100
"Knowledge itself is power"
F.Bacon
Поиск | Карта сайта | Помощь | О проекте | ТТХ  
 Сокровищница
  
 

Фильтр по датам

 
 К н и г и
 
Книжная полка
 
 
Библиотека
 
  
  
 


Поиск
 
Поиск по КС
Поиск в статьях
Яndex© + Google©
Поиск книг

 
  
Тематический каталог
Все манускрипты

 
  
Карта VCL
ОШИБКИ
Сообщения системы

 
Форумы
 
Круглый стол
Новые вопросы

 
  
Базарная площадь
Городская площадь

 
   
С Л С

 
Летопись
 
Королевские Хроники
Рыцарский Зал
Глас народа!

 
  
ТТХ
Конкурсы
Королевская клюква

 
Разделы
 
Hello, World!
Лицей

Квинтана

 
  
Сокровищница
Подземелье Магов
Подводные камни
Свитки

 
  
Школа ОБЕРОНА

 
  
Арсенальная башня
Фолианты
Полигон

 
  
Книга Песка
Дальние земли

 
  
АРХИВЫ

 
 

Сейчас на сайте присутствуют:
 
  
 
Во Флориде и в Королевстве сейчас  15:40[Войти] | [Зарегистрироваться]

Парсер математических выражений с трансляцией в промежуточный байт-код

Дима Швейкус
дата публикации 01-04-2005 08:36

Вместо предисловия

Иногда в программе требуется произвести серию каких-либо однотипных расчётов. При этом очень желательно, чтобы формулу, по которой производятся вычисления, можно было задавать в процессе выполнения программы. Естественным решением является использование модуля, который бы распознавал формулу в строке и выполнял бы её. Одним из вариантов является использование интерпретатора, который анализирует формулу и выполняет её. Недостатком этого подхода является низкая скорость, если значение выражения требуется находить много(ну , навскидку ~100 000) раз. Когда передо мной встала эта проблема, то пришлось писать свой компилятор. Ну, компилятором это, конечно, можно назвать лишь с натяжкой. В процессе выполнения программа генерирует «скрипт» формулы, который затем и выполняет.

Использование

Основным является модуль UMathParser.pas, в котором реализован класс TMathParser. Само использование проходит в 2 этапа:

  1. трансляция формулы в промежуточный байт-код.
  2. выполнение этого кода.
Первый шаг выполняется функцией function Translate(Expr,vars:string):boolean; где Expr – формула, заданная в виде строки. Например: sin(x) + 5^y и т.п. а vars – список переменных через запятую; может быть пустым, если использование таковых не предвидится. В предыдущем примере это было бы: “x,y”.

Если в формуле нет ошибок, то результатом функции будет true, иначе возникнет Exception. Наличие байт-кода для выполнения указывает свойство :

property Translated: Boolean Если первый шаг прошёл успешно, то можно переходить ко второму.

Значения переменных можно изменять с помощью свойств
  • property Vars[index:integer]:TDouble;
    обращение к переменной по её индексу. ПЕРЕМЕННЫЕ НУМЕРУЮТСЯ НАЧИНАЯ С 1 А НЕ С 0
  • property VarByName[VarName:string]:TDouble
    обращение к переменной по имени. Этот способ медленнее первого.
  • property VarName[index:integer]:string
    этот свойство хранит имена переменных. После этого можно выполнять байт-код следующими фунциями
  • function Get: Extended;
    Выполняет байт код и возвращает полученное значение.
  • function Get(Vars:TVector): Extended;
    то же самое, что и предыдущее, только перед выполнением переменным присвоятся соответствующие значения элементов вектора Vars;
    TVector – динамический массив длиной [число_переменных + 1] (VarsCount +1). Тип TVector описан в модуле UParseTypes.pas. Здесь переменные нумеруются с 1 а не с 0, то есть у ‘x’ с нашего примера индекс 1 а у ’y’ соответственно 2.
  • function Get(Vars:array of const): Extended; -тоже иногда удобно. Следует заметить, что это выполняется несколько медленнее , чем предыдущий метод. Зато не надо инициализировать динамический массив.
Прочее:
function AddUserFunc( FuncName:string; 
                      ParameterCount:Integer;
                      Func:TUserFunc):boolean;
Добавляет новую функцию с имененем FuncName, и числом параметров ParameterCount.
Тип
type TUserFunc = function(X:TVector):TDouble;
описан в модуле UParseTypes.pas. Возвращает true, если функция добавлена. Функция должна вызываться до того, как будет сгенерирован байт-код.

Пример.

procedure TForm1.Button1Click(Sender: TObject);
var P:TMathParser;
    i,count:integer;
    a,b,x:extended;
    VX:TVector;
begin
  P:=TMathParser.create;
  P.Translate(edFunc.Text,edVar.Text);
  {создаём байт-код}
  if P.Translated then //если код создан,
  begin //то выполняем его
    SetLength(VX,2);
    Chart1.Series[0].Clear;
    a:=StrToFloat(edA.Text);
    b:=StrToFloat(edB.Text);
    count:=edCount.Value;
    for i:=0 to Count-1 do
    begin
      x:=a+(b-a)*i/(count-1);
      P.Vars[1]:=x;
      Chart1.Series[0].AddXY(x,P.Get);
     {тут собственно находим значение функции в i-й точке и отрисовываем его}
    end;
  end;
end;

блок
VX[1]:=x;
Chart1.Series[0].AddXY(x,P.Get(VX));
Можно было бы заменить на
Chart1.Series[0].AddXY(x,P.Get([x]));
Или
P.Vars[1]:=x;
Chart1.Series[0].AddXY(x,P.Get);

Замечание:

  1. Модуль поставляется as is, так что используете его вы на свой страх и риск.
  2. Писательский дар у меня отсутствует напрочь, так что если что непонятно – смотрите исходники примеров, или исходники самого компилятора.



К материалу прилагаются файлы:


Смотрите также материалы по темам:
[Разбор и вычисление выражений]

 Обсуждение материала [ 24-07-2005 20:37 ] 22 сообщения
  
Время на сайте: GMT минус 5 часов

Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter.
Функция может не работать в некоторых версиях броузеров.

Web hosting for this web site provided by DotNetPark (ASP.NET, SharePoint, MS SQL hosting)  
Software for IIS, Hyper-V, MS SQL. Tools for Windows server administrators. Server migration utilities  

 
© При использовании любых материалов «Королевства Delphi» необходимо указывать источник информации. Перепечатка авторских статей возможна только при согласии всех авторов и администрации сайта.
Все используемые на сайте торговые марки являются собственностью их производителей.

Яндекс цитирования