Консультация № 184065
22.09.2011, 15:19
60.71 руб.
0 21 1
Уважаемые эксперты!
Помогите пожалуйста выполнить задание:

Вот имеется модуль
Код:

unit droby;
interface
type
natur = 1..high(longint);
frac = record
p : longint; {числитель дроби}
q : natur {знаменатель дроби}
end;

procedure sokr(var a: frac);
procedure summa(a, b: frac; var c: frac);
procedure raznost(a, b: frac; var c: frac);
procedure proizvedenue(a, b: frac; var c: frac);
procedure chastnoe(a, b: frac; var c: frac);
procedure stepen(a: frac; n : natur; var c: frac);

{раздел реализации модуля}
implementation

{нахождение наибольшего общего делителя (нод) двух чисел - вспомогательная функция}
function nodevklid(a, b: natur): natur;
begin
while (a <> b) do
if (a > b) then
if (a mod b <> 0)
then
a := (a mod b)
else
a := b
else
if (b mod a <> 0)
then
b := (b mod a)
else
b := a;
nodevklid := a
end;

procedure sokr; {сокращение дроби}
var m, n : natur;
begin
if (a.p <> 0)
then begin
if (a.p < 0)
then
m := abs(a.p)
else
m := a.p; {совмещение типов, т.к. a.p - longint}
n := nodevklid(m, a.q);
a.p := a.p div n;
a.q := a.q div n
end
end;

procedure summa; {сумма дробей}
begin
c.q := (a.q * b.q) div nod(a.q, b.q);
c.p := a.p * c.q div a.q + b.p * c.q div b.q;
sokr(c)
end;

procedure raznost; {разность дробей}
begin
c.q := (a.q * b.q) div nod(a.q, b.q);
c.p := a.p * c.q div a.q - b.p * c.q div b.q;
sokr(c)
end;

procedure proizvedenue; {умножение дробей}
begin
c.q := a.q * b.q;
c.p := a.p * b.p;
sokr(c)
end;

procedure chastnoe; {деление дробей}
begin
c.q := a.q * b.p;
c.p := a.p * b.q;
sokr(c)
end;

procedure stepen; {возведение в степень}
var i : natur;
begin
c.q := 1;
c.p := 1;
sokr(a);
for i := 1 to n do
proizvedenue(a, c, c)
end;

{раздел инициализации модуля}
begin
end.


Нужно сделать программу:
Ввести арифметическое выражение с простыми дробями и выполнить действия.

Обсуждение

давно
Профессор
230118
3054
22.09.2011, 15:46
общий
22.09.2011, 16:13
Тут все операции реализованы. В чем нужна помощь?
Неизвестный
22.09.2011, 16:13
общий
Адресаты:
Цитата: Асмик Гаряка
Тут все операции реализованы. В чем нужна помощь?
Видимо, нужен разбор вводимой пользователем строки и реализация вычислений.
Цитата: Асмик Гаряка
Хотя сумму и разность явно сделаны неправильно.
А с этого места можно поподробнее? В чем "неправильность"?
давно
Профессор
230118
3054
22.09.2011, 16:14
общий
это ответ
Здравствуйте, Посетитель - 372181!
Эта программа использует данный модуль.
Это рекурсивный парсер арифметических выражений со скобками.
drobtostr получает строковую запись дроби.
alltrim удаляет пробелы.
parentheses находит выражение внутри скобок.
eval вычисляет значение выражения.

Код:
program drob;

{$APPTYPE CONSOLE}

uses
SysUtils,
droby in 'Projects\droby.pas';

function drobtostr(a:frac):string;
var s,s1:string;
begin
str(a.p,s);
s:=s+'/';
str(a.q,s1);
drobtostr:=s+s1;
end;

procedure printdrob(a:frac);
begin
write(drobtostr(a));
end;


function alltrim (s:string):string; {?????? ???????}
var p:integer;
begin
repeat
p:=pos(' ',s);
if p>0 then delete (s,p,1);
until p=0;
alltrim:=s;
end;

function parentheses(s:string):string;
var k,bracketcount,open,close:integer;
begin
k:=pos('(',s);
bracketcount:=0;

if k=0 then begin parentheses:='' ;exit end
else
begin
open:=k;
while k<=Length(s) do
begin
if s[k]='(' then inc(bracketcount);//otryvayuwaya
if s[k]=')' then begin dec(bracketcount); close:=k; end; //zakryvayuwaya
inc(k);
end;
if bracketcount<>0 then begin write('wrong expression');parentheses:='';exit end;
parentheses:=copy(s,open+1,close-open-1);
end;
end;



function eval(s:string):frac;
var simple:boolean;
k,c:integer;
p:longint;
q : natur;
r1, r2,r3:frac;
sub,sub1:string;
begin
k:=pos('(',s);
sub:=parentheses(s);
if sub<>'' then
begin
r1:=eval(sub);
sub1:=drobtostr(r1);
Delete(s,k,Length(sub)+2);
Insert(sub1,s,k);
eval:=eval(s);
exit;
end;
k:=pos('+',s)+pos('-',s)+pos('*',s);
if k=0 then
begin
simple:=true;
end;
if simple then
begin
k:=pos('/',s);
if k=0 then
begin
writeln('wrong expression');
eval.p:=0;
eval.q:=1;
end
else
begin
Val(copy(s,1,k-1),p,c);
eval.p:=p;
Val(copy(s,k+1,Length(s)-k),q,c);
if (q>0) then
eval.q:=q
else
writeln('wrong expression');
end
end
else
begin
k:=pos('+',s);
if (k>1) then
begin
r1:=eval(copy(s,1,k-1));
r2:=eval(copy(s,k+1,Length(s)-k));
summa(r1,r2,r3);
eval:=r3;
exit;
end;
k:=pos('-',s);
if (k>1) then
begin
r1:=eval(copy(s,1,k-1));
r2:=eval(copy(s,k+1,Length(s)-k));
raznost(r1,r2,r3);
eval:=r3;
exit;
end;
k:=pos('*',s);
if (k>1) then
begin
r1:=eval(copy(s,1,k-1));
r2:=eval(copy(s,k+1,Length(s)-k));
proizvedenue(r1,r2,r3);
eval:=r3;
exit;
end;
k:=pos('/',s);
if (k>1) then
begin
r1:=eval(copy(s,1,k-1));
r2:=eval(copy(s,k+1,Length(s)-k));
chastnoe(r1,r2,r3);
eval:=r3;
exit;
end;
end;

end;

var r:frac;
v1:string;

begin
write('vvedite vyrazhenie');
readln (v1);
v1:=alltrim(v1);
r:=eval(v1);
printdrob(r);
readln;
end.
давно
Профессор
230118
3054
22.09.2011, 16:16
общий
Разобралась, правильно
давно
Профессор
401888
1232
22.09.2011, 16:20
общий
Я так понимаю что нужно
Ввести арифметическое выражение с простыми дробями и выполнить действия.

Т.е, на вводе типа
(3/6+2/7)*(1/4-3/5)
Неизвестный
22.09.2011, 17:10
общий
Да, имеено так и надо.
давно
Профессионал
304622
583
22.09.2011, 18:23
общий
Цитата: 187336
Т.е, на вводе типа
(3/6+2/7)*(1/4-3/5)


Да, имеено так и надо.


Хм-м! А то, что тут надо рекурсивный алгоритм использовать, вас не смущает? Может быть имеется в виду выражение с одним арифметическим действием?
Неизвестный
22.09.2011, 18:47
общий
Адресаты:
Нет, это вряд ли, что с одним арфм. действием.
давно
Старший Модератор
31795
6196
22.09.2011, 20:54
общий
Адресаты:
Цитата: Сергей Бендер
А то, что тут надо рекурсивный алгоритм использовать, вас не смущает?

Можно просто обойтись польской записью, без всякой рекурсии.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

Неизвестный
22.09.2011, 21:12
общий
Адресаты:
опишите пожалуйста команды, чтобы разобраться за что они отвечают?
давно
Профессионал
304622
583
23.09.2011, 12:37
общий
Адресаты:
Можно просто обойтись польской записью


Это как сказать! Форма записи задаётся постановкой задачи. Раз предписано вводить "(3/6+2/7)*(1/4-3/5)", то как ни вертись, а именно это и надо разбирать.
давно
Старший Модератор
31795
6196
23.09.2011, 12:52
общий
Адресаты:
Цитата: Сергей Бендер
то как ни вертись, а именно это и надо разбирать.

Разбирать нужно, но введенную строку также нужно ещё анализировать на правильность заданых параметром, т.е. без построения семантического анализатора тут не обойтись.
После анализа получится польская запись: (<A>+<B>)*(<C>+<D>)=<A><B>+<C><D>+*=<E><C><D>+*=<E><F>*=<R>
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Профессионал
304622
583
23.09.2011, 13:05
общий
Адресаты:
М-м-м! Для этого не нужен рекурсивный алгортим?
давно
Старший Модератор
31795
6196
23.09.2011, 13:08
общий
Адресаты:
Нет не нужен. Вы думаете, что компиляторы разбирая различного рода матвыражения в программах "прыгают" в рекурсию?
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Профессионал
304622
583
23.09.2011, 13:38
общий
23.09.2011, 13:39
Адресаты:
Не знаю. На такой уровень мне не доводилось заходить. Знаю только, что алгортимы по сути эквивалентные рекурсии, могут быть реализованы без её явного использования, обычными средствами: циклами, массивами и т.д. Может быть что-то такое и происходит? Или это принципиально иной алгоритм?
Неизвестный
25.09.2011, 18:46
общий
Адресаты:
Сделаейте пожалуйста еще с одним арифметич. действием. Оч прошу.
давно
Профессор
230118
3054
25.09.2011, 18:53
общий
Если с делением, то все совершенно аналогично.
давно
Профессор
230118
3054
25.09.2011, 18:56
общий
Добавляем
# k:=pos('-',s);
# if (k>1) then
# begin
# r1:=eval(copy(s,1,k-1));
# r2:=eval(copy(s,k+1,Length(s)-k));
# raznost(r1,r2,r3);
# eval:=r3;
# exit;
# end;
Делаем замену 2 местах. - на : и raznost на chastnoe
давно
Старший Модератор
31795
6196
26.09.2011, 17:04
общий
26.09.2011, 21:45
Адресаты:
Компиляторы при анализе матвыражений переводят их в промежуточную запись, по нескольким причинам:
- это машинно-независимая запись;
- независит от особенностей используемого языка программирования;
- появляется возможность применять к выражениям специальные алгоритмы оптимизации;
- отсутствие скобк, что очень важно для процессора, т.к. у него их тоже нет.

Существует несколько их видов, но широкое распространение получила польская запись(ПЗ).
Вот пример перевода в польскую запись(http://www.pascal.hop.ru/):
[code h=200]Const OperSet:set of char= ['+','-','*','/','(',')','^'];
Var Stack : array [1..10] of Char;
InputString : String;
OutputString : String;
I,J,K, TopStack : Word;
Symb : Char;
SymbTmp : Char;
Function ChToInt(C:Char):Integer;
Var Result : Integer;
Begin
Case C Of
'(': Result := 0;
')': Result := 1;
'-': Result := 2;
'+': Result := 2;
'*': Result := 3;
'/': Result := 3;
'^': Result := 4;
End;
ChToInt := Result;
End;
Function PRCD(Ch1, Ch2 : Char):Boolean;
Var Result : Boolean;
Begin
If ChToInt(Ch1) >= ChToInt(Ch2) Then Result := True
Else Result := False;
If Ch2='(' Then Result := False;
If Ch1='(' Then Result := False;
If (Ch2=')') And (Ch1<>'(') Then Result := True;
If (Ch2='(') And (Ch1=')') Then Result := False;
PRCD := Result;
End;
Function Empty:Boolean;
Var Result : Boolean;
Begin
If TopStack = 0 Then Result := True
Else Result := False;
Empty := Result;
End;
Function TopStackSymb:Char;
Begin
TopStackSymb := Stack[TopStack];
End;
Procedure Push(Ch : Char);
Begin
Inc(TopStack);
Stack[TopStack] := Ch;
End;
Function Pop:Char;
Begin
Pop := Stack[TopStack];
Stack[TopStack] := ' ';
Dec(TopStack);
End;
Begin
ClrScr;
TopStack := 0;
OutputString := '';
ReadLn(InputString);
For I := 1 To 10 Do Stack[I] := ' ';
For I := 1 To Length(InputString) Do Begin
Symb := InputString[I];
If Symb IN OperSet
Then Begin
While Not(Empty) And PRCD(TopStackSymb,Symb) Do Begin
SymbTmp := Pop;
OutputString := OutputString + SymbTmp;
End;
If (Empty) Or (Symb<>')') Then Push(Symb)
Else SymbTmp := Pop;
End Else OutputString := OutputString + Symb;
End;
While Not(Empty) Do Begin
SymbTmp := Pop;
OutputString := OutputString + SymbTmp;
End;
WriteLn(InputString);
WriteLn(OutputString);
ReadLn;
End.[/code]
И пример её работы:

Практически, это уже почти готовый байт-код для процессора или другого исполнительного механизма(к примеру генератора кода).

Думаю Вы знакомы с принципами работы сопроцессора х86(аппаратная реализация принципа ПЗ). С точки зрения сопроцесора сгенерированный код будет таким:
FLD a ;st(0)=a
FLD b ;st(0)=b, st(1)=a
FADD ;st(0)=st(0)+st(1)=e, st(1)=nil
FLD c ;st(0)=c, st(1)=e
FLD d ;st(0)=d, st(1)=c, st(2)=e
FADD ;st(0)=st(0)+st(1)=f, st(1)=e, st(2)=nil
FMUL ;st(0)=st(0)*st(1)=g, st(1)=nil

где
FLD - загрузить значение
FADD - суммировать два регистра стека сопроцессора
FMUL - умножить два регистра стека сопроцессра

Увидели разницу с ПЗ : ab+cd+*, практически одинаковы. Но если в аппаратной реализации сопроцессора тольлко восемь стековых регистров, то в программной, их количество может быть ограниченно только доступной программе памятью.

Процессору совершенно безразлично имена переменных используемые программистом, для него это адрес переменной в памяти и её размер(1, 2 и т.д. байт). Компилятору названия переменных также безразличны, главное, чтобы они не совпадали с зарезервированными словами, в памяти формируется массив соответствия название_переменной=её_адрес и при компиляции в соответствующие команды процессора подставляются адреса этих переменных.

Если расматривать с точки зрения задания, то дробь число:число(есть и такой вариант записи дроби) можно представить как одну переменную-запись и сооветсвенно запомнить её значение в некотором массиве, а в самом выражении использовать индекс этого массива. Получить из исходного выражения польскую запись и запустить его на испольнительный механизм, который будет считать окончательное значение.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Старший Модератор
31795
6196
26.09.2011, 17:55
общий
Адресаты:
И ещё добавлю.
Сейчас для разбора выражений можно использовать любые алгоритмы, доступная память практически ничем не ограничена. Но раньше было доступно только один мегабайт(16-ть сегментов по 64-е килобайта). Из них старшие 6-ть сегментов заняты видеобуфером и BIOS, пара младших служебной информацией BIOS и DOS. На долю компилятора остается максимум 500 килобайт для своего кода, исходного текста программы и результата компиляции. Под стек можно было отвести только один сегмент. Из-за ограничений по стеку программы использование рекурсивных(очень сильно нагружает стек, т.к. хранятся передаваемые параметры и адреса возврата из п/программ) алгоритмов было нецелесообразно и их заменили итерационными.
Об авторе:
Мне безразлично, что Вы думаете о обо мне, но я рад за Вас - Вы начали думать.

давно
Профессионал
304622
583
26.09.2011, 21:43
общий
Адресаты:
Спасибо. Буду вникать.
Форма ответа