Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
4 основи програмування книга.doc
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
1.77 Mб
Скачать

14.5. Проектування модулів. Модуль rat

Модуль RAT містить всі засоби, що забезпечують застосування у програмах раціональних чисел. Сюди входять:

  • описання типу Rational, констант типу Rational;

  • процедури введення-виведення раціональних чисел;

  • функції перетворення числових типів і типу Rational;

  • функції арифметичних операцій і порівнянь раціональних чисел;

  • засоби, що використовуються для реалізації вищевказаних процедур (внутрішні засоби модуля).

Проектування почнемо з визначення поняття раціонального числа. Раціональні числа – це дроби виду Num/Den, де Num – чисельник, а Den – знаменник. Для забезпечення коректності і єдності представлення раціонального числа у виді пари <Num, Den> (дробу Num/Den) нехай виконуються наступні обмеження:

Den > 0, НОД(Num, Den) = 1, Якщо Num = 0, то Den = 1

Таке представлення ми будемо називати канонічною формою. Дії над дробами природно і зручно реалізовувати у виді функцій. Але функції мови Pascal повертають скалярні значення. Тому представлення дробу у виді запису у цьому випадку непридатне. Для того, щоб обійти цю чисто лінгвістичну перешкоду, уточнимо тип Rational як посилання на запис:

Type

Rational = ^RatValue;

RatValue = Record

Num, {чисельник }

Den: LongInt {знаменник}

End;

{Тип LongInt - один із цілочисельних типів в TP-6. Множина значень визначена константою MaxLongInt = 231 - 1.}

Нижче описані деякі (далеко не всі) засоби RAT, необхідні для роботи з раціональними числами. Ключовою функцією модуля є функція CanRat, що приводить дріб до канонічної форми. У свою чергу, CanRat використовує цілочисельну функцію GCD, яка обчислює найбільший спільний дільник (НСД) двох натуральних чисел.

A / B = A div НСД(А, В) / В div НСД(А, В)

{------------НСД двох натуральних чисел-------------------------}

Function GCD(u, v: LongInt): LongInt;

Begin

While (u <> 0) and (v <> 0) do

If u > v

then u := u mod v

else v := v mod u;

If u = 0

then GCD := v

else GCD := u

End;

{------------канонізація раціонального числа--------------------}

Function CanRat(X: Rational): Rational;

Var

u, v, w: LongInt;

Begin

u := X^.Num;

v := X^.Den;

If v = 0

then CanRat := Nil {дріб із нульовим знаменником}

else begin

If v < 0 {знаменник > 0}

then begin

u := -u;

v := -v end;

w := GCD(Abs(u), v);

If w <> 1

then begin {скорочення чисельника і знаменника}

u := u div w;

v := v div w end;

New(R);

Z^.Num := u;

Z^.Den := v ;

CanRat := Z;

end

End;

Як уже відзначалось, процедури введення-виведення потребують дуже старанного проектування, що враховує багато факторів (зручність використання, захист від несанкціонованих дій і т.д.). Ми опишемо тільки їх тривіальні макети.

{ВВЕДЕННЯ-ВИВЕДЕННЯ}

Procedure RatInp(var X: Rational); {Введення}

Var

Ch: Char;

Begin

New(X);

Write('введення чисельника '); Read(X^.Num);

Ch := ReadKey;

If Ch = '/'

then begin

Write('введення знаменника '); Read(X^.Den);

X := CanRat(X)

end

else X^.Den := 1

End;

Procedure RatPrint(X: Rational); {Виведення}

Begin

If X = Nil

then Write('Ділення на нуль')

else begin

Write(X^.Num);

If X^.Den <> 1

then begin Write('/'); Write(X^.Den) end;

end;

Writeln

End;

{АРИФМЕТИЧНІ ОПЕРАЦІЇ}

Function AddRat(X, Y: Rational): Rational; {Додавання}

Begin

New(Z);

Z^.Num := X^.Num*Y^.Den + X^.Den*Y^.Num;

Z^.Den := X^.Den*Y^.Den;

AddRat := CanRat(Z)

End;

Function SubRat(X, Y: Rational): Rational; {Віднімання}

Begin

New(Z);

Z^.Num := X^.Num*Y^.Den - X^.Den*Y^.Num;

Z^.Den := X^.Den*Y^.Den;

SubRat := CanRat(Z)

End;

Function MultRat(X, Y: Rational): Rational; {Множення}

Begin

New(Z);

Z^.Num := X^.Num*Y^.Num;

Z^.Den := X^.Den*Y^.Den;

MultRat := CanRat(Z)

End;

Function DivRat(X, Y: Rational): Rational; {Ділення}

Begin

If Y^.Num = 0

then DivRat := Nil

else begin

New(Z);

Z^.Num := X^.Num*Y^.Den;

Z^.Den := X^.Den*Y^.Num;

DivRat := CanRat(Z)

end

End;

Function MinusRat(X: Rational): Rational; {число з знаком мінус}

Begin

New(Z);

Z^.Num := -X^.Num;

Z^.Den := X^.Den;

MinusRat := Z

End;

{ПОРІВНЯННЯ}

Function EquRat(X, Y: Rational): Boolean; {Рівність}

Begin

EquRat := (X^.Num = Y^.Num) and (X^.Den = Y^.Den)

End;

Function GrtRat(X, Y: Rational): Boolean; {Порівняння на "більш"}

Begin

GrtRat := SubRat(X, Y)^.Num > 0

End;

{ПЕРЕТВОРЕННЯ ТИПІВ}

Function RatReal(X:Rational): Real; {Раціональні в дійсні}

Begin

RatReal := X^.Num/X^.Den

End;

Function IntRat(X: LongInt): Rational; {Ціле в раціональне}

Begin

New(Z);

Z^.Num := X; Z^.Den := 1;

IntRat := Z

End;

Кожна з функцій, що описані вище, реалізує одну з дій у відповідності з його математичним визначенням. Так, наприклад, додавання спирається на формулу

A/B + C/D = CanRat((A*D + B*C)/(B*D))

Оскільки модуль планується використовувати при програмуванні багатьох прикладних задач, особливу увагу треба приділити його оптимізації. З цією метою необхідно, в першу чергу, виділити і оптимізувати ключеві засоби модуля. У нашому прикладі це функції GCD і CanRat.

Ще один шлях підвищення ефективності – зменшення середньої кількості викликів функції CanRat у функціях модуля. У функції AddRat, наприклад, це можна зробити, розглянувши поряд з загальною формулою додавання її окремі випадки, в яких виклик CanRat не потрібний. Попутно зменшується і кількість арифметичних операцій. На справді, при визначенні додавання виділимо окремі випадки додавання дробу і цілого числа, а також дробів з рівними знаменниками

A/B + C/D = CanRat((A*D + B*C)/(B*D)); (*)

A/B + C/B = CanRat((A + C)/B);

A/B + C = (A + B*C)/B;

A + C/D = (A*D + C)/D

Функція AddRat, що реалізована у відповідності з цим визначенням, у середньому ефективніше, ніж описана у модулі.