Парсер математических операций

Общие вопросы: версии и диалекты, синтаксис языка, cтруктуры и типы данных (массивы, строки, списки...), обработка данных и т.д.
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

18 мар 2009, 15:47

Прочитать со стандартного ввода арифметическое выражение.
(Пускай это будет строка)
В нем могут содержаться операции +, -, *, /, exp, ln, sin, cos, числовые константы и переменная x.
Выражение необходимо представить в виде дерева, листья которого – числа или переменные, а внутренние узлы – операции (при этом есть у операции только один аргумент, то один из сыновей может быть nil (пускай это будет правый)). Результатом должна быть структура указателей, описанная так ,как предыдущая, но не обязательно дерево. Если с правой стороны равенства находятся подвыражения исходного выражения, то не стоит создавать в памяти новых узлов, а использовать уже существующие. В частности, это относится ко всему выражению exp(A). Прочитанное выражение и результат необходимо держать в программе в виде (псевдо)дерева.

Да, конечно, что-то много понаписал. Для начала нужно просто написать процедуру (или функцию) со стринговым входным параметром, содержащим строку ввода и чтоб выходным было дерево.
Например, для выражения: (a-(c+d))*(e/(f-g))
*
/ \
/ \
- :
/ \ / \
a + e -
/ \ / \
c d f g
Потом, насколько я понимаю, нужна процедура прямого обхода дерева для получения префиксной формы выражения.
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

18 мар 2009, 16:27

За основу для процедуры можно взять это:

Код: Выделить всё

uses crt;
Type Vhod=char;{integer}
     U=^Tree;
     Tree=Record
     Inf:VHOD;
     L,R:U;
End;
var ResTree:U;
    s:string;
    i:byte;
{процедура добавления числа в двоичное дерево}
Procedure InsRec(x:char{integer};Var Tree:U);
Begin
 If Tree = Nil Then
  Begin
   New(Tree);
   Tree^.L := Nil;
   Tree^.R := Nil;
   Tree^.Inf := x
  End
  Else
  If x < Tree^.inf Then
  InsRec(x,Tree^.L)
  Else InsRec(x,Tree^.R);
End;


begin
clrscr;
writeln('Vvedite vyrazhenie: ');
readln(s);
for i:=1 to length(s) do
 begin
  insrec(s[i],ResTree);
 end;
end.
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

18 мар 2009, 20:59

С этим уже можно работать...

Код: Выделить всё

uses Crt;
type TreePointer = ^tree;
 tree = record
 data: char;
 left: TreePointer;
 right: TreePointer;
end;
var
root, dummy: TreePointer;
ch:char;

function STree(root, r:TreePointer; data: char):TreePointer;
 begin
  if r = nil then
   begin
    new(r);
    r^.left := nil;
    r^.right := nil;
    r^.data := data;
    if data < root^.data then root^.left := r
    else root^.right := r;
    STree := r;
   end
   else
    begin
     if data<r^.data then STree := STree(r, r^.left, data)
     else STree := STree(r, r^.right, data)
    end;
 end;

procedure PrintTree(r: TreePointer; n: integer);
var i:integer;
 begin
  if r<>nil then begin
  PrintTree(r^.left, n+1);
  for i := 1 to n do write('   ');
  Writeln(r^.data);
  PrintTree(r^.right, n+1);
 end;
end;

begin
 clrscr;
 root := nil;
 repeat
 Write('Vvodite dannye (dlia zavershenia vvoda nazhmite probel) -> ');
 ch := ReadKey;
 Writeln(ch);
 if root= nil then root := STree(root, root, ch)
 else dummy := STree(root, root, ch);
 ch := UpCase(ch);
 until ch =' ';
 PrintTree(root, 0);
 readln;
end.
Единственное тут симметричный проход.
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

18 мар 2009, 23:31

Уточню задание:
Пусть дана строка, содержащая выражение в инфиксной форме, где могут содержаться: +, -, *, /, exp, ln, sin, cos.
1. Написать процедуру построения по этой строке дерева выражений.
2. Написать процедуру прямого обхода дерева выражений с составлением списка меток (значений узлов) дерева выражений.
Нет религии выше истины
Аватара пользователя
Naeel Maqsudov
Сообщения: 2551
Зарегистрирован: 20 фев 2004, 19:17
Откуда: Moscow, Russia
Контактная информация:

18 мар 2009, 23:34

А реальное построение дерева - это обязательное условие?
У меня, например, есть рекурсивная функция, которая интерпретирует даже гораздо более сложные выражения. Она не использует деревьев. Но само рекурсивное её выполнение как раз происходит в соответствии с иерархией операций в выражении.
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

18 мар 2009, 23:53

Naeel Maqsudov писал(а):А реальное построение дерева - это обязательное условие?
К сожалению это одно из условий задачи: "прочитанное выражение необходимо держать в программе в виде дерева." А так у меня тоже после 40 минут "гугления" накопилось множество исходников, использующих алгоритм Дейкстры, рекурсивные функции и т.д. Вообщем что-угодно, но нужного - крупицы.
Так что, буду признателен за любую помощь.
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

19 мар 2009, 00:29

Если в строчке нет символов операций, то это "лист", оконечная вершина.
Иначе ищем символ операции с минимальным приоритетом (которая будет выполняться последней) и добавляем в дерево вершину для этой операции. Левое и правое поддеревья для этой вершины находим рекурсивно, для подстроки, остающейся слева и справа от найденного символа операции.
Но при реализации проблема за проблемой. - Не хватает опыта в работе с деревьями. Что называется кто чем может...
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

19 мар 2009, 02:00

Уточнили задачу!
Оказывается:
1.Вводится выражение в префиксной форме: одна строка - одна константа (все константы целочисленные), одна переменная, одна операция (+, -, *, /, exp, ln, sin, cos).
2.Строится дерево этого выражения.
3.Производится обход дерева и формируется линейный список, содержащий промежуточные результаты нахождения производной.
* D(число) = число_0
* D(переменная_x) = переменная_1
* D(A + B) = D(A) + D(B)
* D(A - B) = D(A) - D(B)
* D(A * B) = D(A) * B + A * D(B)
* D(A / B) = (D(A) * B - A * D(B)) / (B * B)
* D(exp A) = (exp A) * D(A)
* D(ln A) = (число_1 / A) * D(A)
* D(sin A) = (cos A) * D(A)
* D(cos A) = (число_0 - sin A) * D(A)
Последний элемент - производная всего выражения. Если производная не найдена выводится сообщение.
(Если с правой стороны равенства находятся подвыражения исходного выражения, то не стоит создавать в памяти новых узлов, а использовать уже существующие. В частности, это относится ко всему выражению exp(A).Важно: нужно только раз (конкретное число раз) проходить по определенному фрагменту дерева (чтобы это было возможно, фрагменты, которые выступают несколько раз, должны быть «подцеплены» в нескольких местах)).
4.Освобождаются неиспользуемые узлы дерева выражения.
5.Выводятся узлы дерева выражения, и их производная в зависимости от операции.
Например:
>2 0
>4 0
>2+4 0
и т.д.
Так же должно быть выведенно число узлов в памяти перед нахождением производной, после (но перед удалением из памяти ненужных уже узлов) и после удаления этих узлов.
6.Освобождается вся память.
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

19 мар 2009, 13:46

Но удается строить дерево только при вводе выражения в инфиксной форме, а надо в префиксной. Причем число может состоять больше чем из одной цифры, а так же должны быть exp, ln, sin, cos. По идее считывать надо из файла *.in, где каждая строка одна константа, один оператор (+, -, *, /, exp, ln, sin, cos) или переменная x, но думаю просто написать процедуру которая формировала бы строку из этого файла.
Возможно ли всё организовать таким образом?

Код: Выделить всё

uses Crt;
type TreePointer = ^tree;
 tree = record
 data: string;
 left: TreePointer;
 right: TreePointer;
end;

const
  MAXPRIORITY=3;
  SIGNS=['+','-','*','/'];

var
root, dummy: TreePointer;
ch:char;
stroka:string;

function PriorSimv (sign: char): integer;
begin
  case sign of
    '+','-': PriorSimv:=0;
    '*','/': PriorSimv:=1;
  end;
end;

function PriorStr (f:string): integer;
begin
 if (f='ln') or (f='exp') or (f='cos') or (f='sin')
 then PriorStr:=0;
end;

function BuildTree (str: string): TreePointer;
var
  tmp: TreePointer;
  i: integer;
  priormin, imin: integer;
  leftstr, rightstr: string;
  flag:boolean;
begin
 if length(str)=1 then
  begin
   New(tmp);
   tmp^.data:=str[1];
   tmp^.left:=nil;
   tmp^.right:=nil;
   BuildTree:=tmp;
  end
  else
   begin
    i:=length(str);flag:=false;
    priormin:=MAXPRIORITY;
    imin:=i;
    while i>=1 do
     begin
      if ((str[i] in SIGNS) and (PriorSimv(str[i])<priormin)) then
       begin
        priormin:=PriorSimv(str[i]);
        imin:=i;
       end;
      i:=i-1;
     end;
    New(tmp);
    tmp^.data:=str[imin];
    leftstr:=Copy(str,1,imin-1);
    rightstr:=Copy(str,imin+1,length(str)-imin);
    tmp^.left:=BuildTree(leftstr);
    tmp^.right:=BuildTree(rightstr);
    BuildTree:=tmp;
  end;
end;

procedure Infiks(r:TreePointer);
 begin
  if r<>nil then begin
  Infiks(r^.left);
  Write(r^.data);
  Infiks(r^.right);
 end;
end;

procedure Prefiks(r:TreePointer);
 begin
  if r<>nil then
   begin
    Write(r^.data);
    Prefiks(r^.left);
    Prefiks(r^.right);
   end;
 end;

procedure Postfiks(r:TreePointer);
 begin
  if r<>nil then
   begin
    Postfiks(r^.left);
    Postfiks(r^.right);
    Write(r^.data);
   end;
 end;

begin
 clrscr;
 root := nil;
 writeln('Vvedite stroku: ');
 readln(stroka);
 root := BuildTree(stroka);
 writeln('Priamoi obhod: ');
 Prefiks(root);
 writeln;
 writeln('Obratnyi obhod: ');
 Postfiks(root);
 writeln;
 writeln('Simmetrichnyi obhod: ');
 Infiks(root);
 readln;
end.
Нет религии выше истины
dr.Jekill
Сообщения: 509
Зарегистрирован: 03 янв 2009, 23:17
Откуда: Voronezh
Контактная информация:

28 мар 2009, 14:53

С деревьями разобрался. Тему можно закрывать.
Нет религии выше истины
Ответить