|
Вопрос # 267/ вопрос открыт / |
|
Уважаемые эксперты. Мне необходимо вычислить строку вида: "5+3*sin(3*x-8*cos(y)/2)*5^2+8*cos(5)", соответсвенно подставив туда x и y.
Для вычисления я создал пару функций, первая создает дерево вычислений (см. в прилажении), второя подставляем необходимое значение аргумента. А вот как вычисть строку по готовому дереву не могу понять (запутался с указателями). В общем должен работать алгоритм: спуститься по всем ветвям вниз и начать вычисления к веришине, но как это реализовать?
Приложение: Переключить в обычный режим- type
- PFormula = ^TFormula;
- TFormula = packed record
- Oper: string;
- ArgFormulaL: PFormula;
- ArgFormulaR: PFormula;
- ArgL: string;
- ArgR: string;
- end;
- //============================//
-
- function IsSimple(st: string): boolean;
- var
- i: word;
- begin
- result := true;
- for i := 1 to Length(st) do
- if not (st[i] in ['0'..'9', ',',
'A'..'Z', 'a'..'z']) then
- begin
- result := false;
- exit;
- end;
- end;
-
- function IsFunc(st: string; var sFunc, sParam: string; var Oper: word): boolean;
- var
- i, k, L, R: word;
- begin
- result := true;
- k := 0; R := 0;
- L := Pos('(', st);
-
- for i := L to Length(st) do
- begin
- if st[i] = '(' then inc(k);
- if st[i] = ')' then dec(k);
- if k = 0 then
- begin
- R := i;
- break;
- end;
- end;
- if R = Length(st) then Oper := 1
- else Oper := R+1;
- sFunc := Copy(st, 1, L-1);
- sParam := Copy(st, L+1, R-L-1);
- for i := 1 to Length(sFunc) do
- if not(sFunc[i] in ['A'..'Z',
'a'..'z']) then
- begin
- result := false;
- exit;
- end;
- end;
-
- function GetOper(st: string): word;
- var
- i, k: word;
- begin
- result := 1;
- k := 0;
- for i := 1 to Length(st) do
- begin
- if st[i] = '(' then inc(k);
- if st[i] = ')' then dec(k);
- if k = 0 then
- begin
- result := i+1;
- exit;
- end;
- end;
- end;
-
- procedure Info(Formula: PFormula);
- begin
- frMain.Memo1.Lines.Add('TFormula: Pointer = ' + inttostr(integer(Formula)));
- frMain.Memo1.Lines.Add('TFormula: Oper = ' + Formula.Oper);
- frMain.Memo1.Lines.Add('TFormula: ArgL = ' + Formula.ArgL);
- frMain.Memo1.Lines.Add('TFormula: ArgR = ' + Formula.ArgR);
- frMain.Memo1.Lines.Add('TFormula: ArgFormulaL: Pointer = ' +
inttostr(integer(Formula.ArgFormulaL)));
- frMain.Memo1.Lines.Add('TFormula: ArgFormulaR: Pointer = ' +
inttostr(integer(Formula.ArgFormulaR)));
- frMain.Memo1.Lines.Add('');
- Application.ProcessMessages;
- end;
-
- procedure Polski(const st: string; var Formula: TFormula);
- var
- i, Oper: word;
- sFunc, sParam: string;
- begin
- i := 1;
- repeat
-
- if st[i] in ['0'..'9', ','] then inc(i)
- else
-
- if st[i] = '(' then
- begin
- i := GetOper(st);
- Formula.Oper := st[i];
- Formula.ArgL := Copy(st, 2, i-3);
- Formula.ArgR := Copy(st, i+1, Length(st)-i);
-
- if not IsSimple(Formula.ArgL) then
- begin
- New(Formula.ArgFormulaL);
- Polski(Formula.ArgL, Formula.ArgFormulaL^);
- end
- else
- Formula.ArgFormulaL := nil;
-
- if not IsSimple(Formula.ArgR) then
- begin
- New(Formula.ArgFormulaR);
- Polski(Formula.ArgR, Formula.ArgFormulaR^);
- end
- else
- Formula.ArgFormulaR := nil;
-
- Info(@Formula);
- Exit;
- end
- else
-
- if st[i] in ['+', '-', '*',
'/', '^', '%'] then
- begin
- Formula.Oper := st[i];
- Formula.ArgL := Copy(st, 1, i-1);
- Formula.ArgR := Copy(st, i+1, Length(st)-i);
-
- if not IsSimple(Formula.ArgL) then
- begin
- New(Formula.ArgFormulaL);
- Polski(Formula.ArgL, Formula.ArgFormulaL^);
- end
- else
- Formula.ArgFormulaL := nil;
-
- if not IsSimple(Formula.ArgR) then
- begin
- New(Formula.ArgFormulaR);
- Polski(Formula.ArgR, Formula.ArgFormulaR^);
- end
- else
- Formula.ArgFormulaR := nil;
-
- Info(@Formula);
- Exit;
- end
- else
-
- if IsFunc(st, sFunc, sParam, Oper) then
- begin
-
- if Oper = 1 then
- begin
- Formula.Oper := sFunc;
- Formula.ArgL := sParam;
-
- if not IsSimple(Formula.ArgL) then
- begin
- New(Formula.ArgFormulaL);
- Polski(Formula.ArgL, Formula.ArgFormulaL^);
- end
- else
- Formula.ArgFormulaL := nil;
-
- Formula.ArgFormulaR := nil;
- end
-
- else
- begin
- i := Oper;
- Formula.Oper := st[i];
- Formula.ArgL := Copy(st, 1, i-1);
- Formula.ArgR := Copy(st, i+1, Length(st)-i);
-
- if not IsSimple(Formula.ArgL) then
- begin
- New(Formula.ArgFormulaL);
- Polski(Formula.ArgL, Formula.ArgFormulaL^);
- end
- else
- Formula.ArgFormulaL := nil;
-
- if not IsSimple(Formula.ArgR) then
- begin
- New(Formula.ArgFormulaR);
- Polski(Formula.ArgR, Formula.ArgFormulaR^);
- end
- else
- Formula.ArgFormulaR := nil;
- end;
- Info(@Formula);
- Exit;
- end
- else inc(i);
- until i > Length(st);
- end;
-
- procedure SetFormula(Formula: TFormula; Arg, Num: string);
- begin
- if @Formula <> nil then
- begin
- if Formula.ArgL = Arg then
- Formula.ArgL := Num;
- if Formula.ArgFormulaL <> nil then
- SetFormula(Formula.ArgFormulaL^, Arg, Num);
- if Formula.ArgR = Arg then
- Formula.ArgR := Num;
- if Formula.ArgFormulaR <> nil then
- SetFormula(Formula.ArgFormulaR^, Arg, Num);
- Info(@Formula);
- end;
- end;
-
- function Calculate(ArgL, ArgR: extended; Oper: string): extended;
- begin
- result := 0;
- if Oper = '+' then result := ArgL + ArgR;
- if Oper = '-' then result := ArgL - ArgR;
- if Oper = '*' then result := ArgL * ArgR;
- if Oper = '/' then result := ArgL / ArgR;
- if Oper = '%' then result := round(ArgL / ArgR);
- if Oper = '^' then result := Power(ArgL, ArgR);
- if Oper = 'sin' then result := sin(ArgL);
- if Oper = 'cos' then result := cos(ArgL);
- if Oper = 'tan' then result := tan(ArgL);
- if Oper = 'cotan' then result := cotan(ArgL);
- if Oper = 'arccos' then result := arccos(ArgL);
- if Oper = 'arccot' then result := arccot(ArgL);
- if Oper = 'arcsin' then result := arcsin(ArgL);
- if Oper = 'sec' then result := sec(ArgL);
- if Oper = 'lg' then result := log10(ArgL);
- if Oper = 'log' then result := logN(ArgL, ArgR);
- end;
-
- function CalcFormula(Formula: TFormula): extended;
- begin
- //if @Formula <> nil then
- begin
- //?????????????????
- end;
- end;
-
- //================================//
- procedure TfrMain.Button1Click(Sender: TObject);
- var
- st: string;
- Formula: TFormula;
- begin
- Formula.ArgFormulaL := nil;
- Formula.ArgFormulaR := nil;
- st := edit1.Text;
- Polski(st, Formula);
- memo1.Lines.Add('-----------------');
- SetFormula(Formula, 'x', '7');
-
- edit2.text := floattostr(CalcFormula(Formula)); //?????
-
- end;
 |
Вопрос задал: SMaks (статус: 1-ый класс)
Вопрос отправлен: 15 января 2007, 22:00
Состояние вопроса: открыт, ответов: 2.
|
Ответ #1. Отвечает эксперт: Матвеев Игорь Владимирович
Здравствуйте, SMaks!
Вычислять выражение нужно рекурсивно, так же как Вы заменяете x и y на числа в SetFormula().
Кроме того, было еще две ошибки:
1. Calculate() по умолчанию должна возвращать левый параметр, а не ноль, чтобы правильно обрабатывались скобки.
2. SetFormula(), параметр Formala должен быть объявлен как var-параметр, иначе Formala не изменится.
Все это в виде готового проекта в прикреплённом файле. К ответу прикреплён файл. Загрузить » (срок хранения: 60 дней с момента отправки ответа)
Приложение: Переключить в обычный режим- function CalcFormula(Formula: TFormula): extended;
- var
- OprL, OprR : Extended;
- begin
- if @Formula <> nil then
- begin
-
-
- if (Formula.ArgL <> '') and IsSimple(Formula.ArgL)
- then OprL := StrToFloat(Formula.ArgL) else OprL := 0;
-
- if (Formula.ArgR <> '') and IsSimple(Formula.ArgR)
- then OprR := StrToFloat(Formula.ArgR) else OprR := 0;
-
-
-
-
- if Formula.ArgFormulaL <> nil then OprL := CalcFormula(Formula.ArgFormulaL^);
-
- if Formula.ArgFormulaR <> nil then OprR := CalcFormula(Formula.ArgFormulaR^);
-
-
- Result := Calculate(OprL, OprR, Formula.Oper);
- end;
- end;
 |
Ответ отправил: Матвеев Игорь Владимирович (статус: Студент)
Время отправки: 16 января 2007, 08:49
Оценка за ответ: 5
Комментарий к оценке: То что нужно, большое тебе спасибо!
|
Ответ #2. Отвечает эксперт: min@y™
Посмотри, как этот алгоритм реализован в RxLib (модуль Parsing.pas, класс TRxMathParser).
Замена х и у на значения - это совсем просто.
 |
Ответ отправил: min@y™ (статус: Доктор наук)
Время отправки: 16 января 2007, 10:24
Оценка за ответ: 2
Комментарий к оценке: Я спрашивал по конкретной задачи!
|
Мини-форум вопроса
Всего сообщений: 2; последнее сообщение — 17 января 2007, 04:09; участников в обсуждении: 2.
|
SMaks (статус: 1-ый класс), 16 января 2007, 11:59 [#1]:
В заключении, Игорь Владимирович, можешь еще добавить процедурку освобождения всего дерева?
|
|
Матвеев Игорь Владимирович (статус: Студент), 17 января 2007, 04:09 [#2]:
Конечно:
procedure FreeFormula(var Formula: PFormula);
begin
if Formula <> nil then
begin
if Formula.ArgFormulaL <> nil then // Если узел слева не пуст -
FreeFormula(Formula.ArgFormulaL); // проверим его
if Formula.ArgFormulaR <> nil then // Аналогично справа
FreeFormula(Formula.ArgFormulaR);
// Суда попадаем только по выходу из обоих вложенных вызовов,
// что гарантирует, что
// Formula.ArgFormulaL = nil и Formula.ArgFormulaR = nil, поэтому
// дополнительную проверку на это можно не проводить.
try
Dispose(Formula);
Formula := nil;
except
// Пропускаем ошибку EInvalidPointer - Invalid pointer operation.
// Она возникнет, если Вы попытаетесь освободить статическую переменную
on EInvalidPointer do MessageDlg('Ошибка с указателем.'#13#13+
'==> Вы пытаетесь освободить статическую переменную.',
mtError, [mbOk], -1);
end;
end;
end;
Только одно замечание - если Вы освобождаете дерево - само собой корневой узел тоже должен быть динамической переменной, т.е. память под нее должна была быть выделена в run-time (New/GetMem/AllocMem), иначе попытка ее освободить приведет к ошибке. В приведенной функции это исключение блокируется блоком try/except.
Готовый пример, где корень дерева инициализируется в run-time см: (файл удалён)
|
Чтобы оставлять сообщения в мини-форумах, Вы должны авторизироваться на сайте.
|