Главная » Free Pascal » Создание нестандартного модуля Free Pascal

0

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

Начнем со структуры данных. На наш взгляд, для хранения значения дроби лучше всего подходит запись, содержащая два целочисленных поля — числитель и знаменатель:

type

Ratio=record n,d: integer;

end;

Если мы теперь объявим переменную x типа Ratio, то к ее числителю можно обратиться по имени x.n, а к знаменателю — по имени x.d. Конечно, для присвое- ния дроби значения 1/3 можно поступить следующим образом:

x.n:=1;

x.d:=3;

Но в этом случае мы, во-первых, не застрахованы от ситуации x.d=0, которая может привнести к аварийному останову. Во-вторых, нормальные люди сокращают некоторые дроби, если такая возможность имеется (3/6 = 1/2). Наконец, было бы удобнее выполнять такую операцию в одну строку:

x:=Rset(1,3);

Согласитесь, что не очень удобно записывать в программе процедуру сложения дробей x и y по правилам, которые мы учили в школе:

z.n:=x.n*y.d+x.d*y.n;

z.d:=x.d*y.d;

В две строки — это длинновато, а потом после сложения может возникнуть не- обходимость сокращения. Было бы лучше написать функцию сложения двух дро- бей:

z:=Radd(x,y);

Во-первых, это короче. Во-вторых, в функции сложения можно было бы преду- смотреть возможность сокращения результата.

Учитывая соображения подобного рода, мы предлагаем написать группу функ- ций первой необходимости, список которых приведен в табл. 11.2.

Таблица 11.2

Формат вызова

Выполняемое действие

x:=Rset(n1,d1);

Присвоение x=n1/d1 с проверкой d1 0 и сокращением

z:=Radd(x,y);

Сложение дробей с сокращением

z:=Rsub(x,y);

Вычитание дробей с сокращением

z:=Rmult(x,y);

Умножение дробей с сокращением

z:=Rdiv(x,y);

Деление дробей с проверкой x.n 0 и сокращением

Очевидно, что нам понадобятся процедуры для ввода и вывода дробей в при- вычной для пользователя форме:

Rin(x);    // входная информация может иметь вид: 1/3 Rout(x);   // выводить разумно в таком же формате: 1/3

Естественно, что ввод должен был сопровождаться проверкой знаменателя на 0 и предусматривать возможность сокращения. А в процедуре вывода оказалось по- лезным включение дополнительного аргумента — строки, которая могла иденти- фицировать выводимое значение.

Среди неупомянутых операций над дробями мы решили включить в библиоте- ку операцию сравнения дробей и функции прямых и обратных преобразований ме- жду вещественными данными и традиционными дробями. Функция сравнения воз- вращает  положительный  результат,  если  первая  дробь  больше  второй,  нулевой

результат в случае равенства дробей и отрицательный результат, если первая дробь меньше второй.

После всего сказанного представляем на ваш суд модуль Rational, который реализует заявленные выше операции (листинг 11.1). Две функции — определения наибольшего общего делителя (gcd) и сокращения дроби (reduce) — мы решили скрыть от пользователя и не включили их в интерфейсную часть.

Некоторые трудности пришлось преодолеть в процедуре ввода дроби и преоб- разования значений числителя и знаменателя, заданных во введенной строке. Дело в том, что процедура val не умеет переводить отрицательные числа. Пришлось ис- кусственным путем удалять из введенной строки символы "–" и "/". Еще одна по- тенциальная опасность подстерегала нас в реализации процедуры Rout. Сначала было принято решение анализировать знак дроби как знак произведения x.n*x.d. Но в функции преобразования DtoR устанавливаются такие большие значения чис- лителя и знаменателя, что они приводили к переполнению в указанном перемноже- нии. Для сохранения знака числителя пришлось поделить его на abs(x.n).

   Листинг 11.1. М од ул ь  Rational                                               

Unit Rational; INTERFACE

// Общедоступные типы данных type

Ratio=record n,d: integer;

end;

// Общедоступные процедуры и функции procedure Rout(s:string; x:Ratio); procedure Rin(s:string; var x:Ratio); function Rset(n,d:integer):Ratio; function Radd(x,y:Ratio):Ratio; function Rsub(x,y:Ratio):Ratio; function Rmult(x,y:Ratio):Ratio; function Rdiv(x,y:Ratio):Ratio; function Rcomp(x,y:Ratio):integer; function RtoD(x:Ratio):double; function DtoR(x:double):Ratio;

//————————————– IMPLEMENTATION

// Определение НОД

function gcd(x,y:integer):integer; begin

if y=0 then begin Result:=x; exit; end;

Result:=gcd(y,x mod y); end;

// Сокращение дроби

procedure reduce(var x:Ratio); var

n1,d1: integer; begin

n1:=abs(x.n);

if n1=0 then begin x.d:=1; exit; end; d1:=gcd(n1,x.d);

if d1>1 then begin

x.n:=x.n div d1; x.d:=x.d div d1; end;

end;

// Вывод дроби

procedure Rout(s:string;x:Ratio); var

k:integer; begin

write(s);                    // вывод комментария if x.n=0 then

begin writeln(‘0′); exit; end;

k:=(x.n div abs(x.n))* x.d;   // анализ знака дроби if k<0 then write(‘-‘); writeln(abs(x.n),’/’,abs(x.d));

end;

// Ввод дроби

procedure Rin(s:string; var x:Ratio); var

s1,s2: string[20]; i,j: integer;

begin

x.d:=1;

write(s);                    // приглашение ко вводу

readln(s1);                  // ввод дроби-строки вида ‘n/d’

i:=pos(‘/’,s1);              // поиск символа ‘/’

if i<>0 then begin            // есть ли знаменатель? s2:=copy(s1,i+1,10);       // часть строки со знаменателем val(s2,x.d,j);             // перевод знаменателя

delete(s1,i,10);           // удаление знаменателя из s1 end;

if s1[1]=’-‘ then begin       // анализ на ‘-‘ delete(s1,1,1);            // удаление символа ‘-‘

val(s1,x.n,j);             // перевод модуля числителя

x.n:=-x.n;                // смена знака числителя end

else val(s1,x.n,j);           // перевод числителя reduce(x);

end;

// Присвоение дроби значения function Rset(n,d:integer):Ratio; begin

if n*d=0 then begin           // если задан нулевой знаменатель Result.n:=0; Result.d:=1; exit;

end;

Result.n:=abs(n); Result.d:=abs(d); if n*d < 0 then Result.n:=-Result.n;

end;

// Сложение дробей

function Radd(x,y:Ratio):Ratio; begin

Result.n:=x.n*y.d + y.n*x.d; Result.d:=x.d*y.d;

reduce(Result);       // сокращение результата end;

// Вычитание дробей

function Rsub(x,y:Ratio):Ratio; begin

Result.n:=x.n*y.d-y.n*x.d; Result.d:=x.d*y.d; reduce(Result);

end;

// Умножение дробей

function Rmult(x,y:Ratio):Ratio; begin

Result.n:=x.n*y.n; Result.d:=x.d*y.d;

reduce(Result); end;

// Деление дробей

function Rdiv(x,y:Ratio):Ratio; begin

if y.n=0 then begin   // предотвращение деления на 0 writeln(‘Divide by zero’);

readln;

halt;              // завершение работы при делении на 0 end;

Result.n:=x.n*y.d; Result.d:=x.d*y.n; reduce(Result);

end;

// Сравнение дробей

function Rcomp(x,y:Ratio):integer; var

a,b: integer; begin

a:= x.n*y.d; b:= x.d*y.n; if a>b then Result:=1

else if a<b then Result:=-1 else Result:=0;

end;

// Преобразование Ratio в Double function RtoD(x:Ratio):double; begin

Result:=x.n/x.d;

end;

// Преобразование Double в Ratio function DtoR(x:double):Ratio; begin

Result.n:=Round(x*9e7); Result.d:=90000000; reduce(Result);

end; end.

В  качестве  одного  из  тестовых  примеров  была  использована  программа rational_2.pas (листинг 11.2).

   Листинг 1 1 .2 .  Программа  rational_2.pas                                                                                       

program rational_2; uses rational;

var

x,y,z: ratio; d: double;

begin

x:=Rset(2,3);

Rout(‘x=’,x);

y:=Rset(-1,2);

Rout(‘y=’,y);

z:=Radd(x,y);

Rout(‘x+y=’,z);

z:=Rsub(x,y);

Rout(‘x-y=’,z);

z:=Rmult(x,y);

Rout(‘x*y=’,z);

z:=Rdiv(x,y);

Rout(‘x/y=’,z); d:=0.75;

z:=DtoR(d); Rout(‘0.75=’,z);

d:=RtoD(x); writeln(‘2/3=’,d:8:6);

write(‘Input x :’); Rin(x); Rout(‘x=’,x); readln;

end.

Результаты ее работы таковы:

Running "e:\fpc\myprog\rational_2.exe " x=2/3

y=-1/2 x+y=1/6 x-y=7/6 x*y=-1/3 x/y=-4/3

0.75=3/4

2/3=0.666667

Input x :-3/9 x=-1/3

Источник: Кетков, Ю. Л., Свободное программное обеспечение. FREE PASCAL для студентов и школьников, Ю. Л. Кетков, А. Ю. Кетков. — СПб.: БХВ-Петербург, 2011. — 384 с.: ил. + CD-ROM — (ИиИКТ)

По теме:

  • Комментарии