| Россия, г. Москва |
Абстрактный тип данных
5.3. Реализация абстрактного типа
Реализация АТД обычно производится в форме отдельного программного модуля. В заголовочном файле отражается абстрактный взгляд на тип данных. Имена операций конструируются в терминах выбранной абстракции и описываются в терминах, понятных импортеру на уровне, необходимом для импортера. Например, в АТД FRACTION для функции ConvertToFixPoint достаточно указать лишь, что на входе у нее простая дробь, а на выходе - соответствующее ей число с фиксированной точкой. Как выполняется операция, указывать не требуется, так как клиенту это не важно. С другой стороны, для операций должны подробно указываться все пред- и постусловия, необходимые для ее выполнения, в понятных импортеру терминах (как разделяется в результате целая и дробная части, где расположен знак и т.п.).
Утверждения (предусловия, постусловия и инварианты модуля) в модуле определения называют абстрактными утверждениями. Они - суть требования или спецификация модуля как для разработчика, так и для клиента. Все условия, расположенные в модуле реализации, называют утверждениями реализации. Если модуль корректен, то все абстрактные утверждения поддерживаются утверждениями реализации.
Если пользователь обеспечил все абстрактные предусловия и инварианты перед запуском операции, то он вправе ожидать, что после ее выполнения все абстрактные постусловия и инварианты будут выполнены. Разработчик модуля имеет несколько иную точку зрения - он вправе рассчитывать, что все предусловия и инварианты реализации заведомо истинны перед вызовом операции, однако его задача состоит в том, чтобы обеспечить выполнения всех постусловий и инвариантов после завершения операции.
Далее приведем пример реализации абстрактного типа данных - обыкновенная дробь. Она будет состоять из числителя и знаменателя, при этом всегда будет сокращенной. Пусть знаменатель всегда будет положительным, а числитель будет определять знак (положительный или отрицательный) для всей дроби. Остается лишь определить, что нулевая дробь - это дробь 0/1 (т.е. знаменатель равен 1).
В качестве операций следует рассмотреть стандартные арифметические операции - сложение, вычитание, умножение, деление, обязательные для абстрактного типа данных операции сравнения и копирования, операцию создания/инициализации. Дополнительно логично реализовать операцию преобразования к десятичной дроби, операцию построения дроби из целого числа и операции-селекторы - получение числителя и получения знаменателя.
Код будет организован в виде двух файлов - заголовка "fraction.h" и файла реализации "fraction.c":
----- файл fraction.h -----
/*******************************************************
Date: 10 January 2013
Description: fraction type
******************************************************/
typedef struct
{
int numerator;
int denominator;
} Fraction;
/*******************************************************
* Name : initF
* Purpose : initialize fraction
* Input : num – numerator
* denum – denominator
* Output : none,
* wasErr() can be called after,
* to test whether the result is correct
* Return : initialized fraction
******************************************************/
Fraction initF(int num, int denum);
/*******************************************************
* Name : isNullF
* Purpose : test fraction to be 0
* Input : f – fraction
* Output : none
* Return : 1 – if is f=0, 0 – if f!=0
*
******************************************************/
int isNullF(Fraction f);
/*******************************************************
* Name : compareF
* Purpose : compares two fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : 1 – f1<f2,0 – f1=f2, -1 f1<f2
******************************************************/
int compareF(Fraction f1, Fraction f2);
/*******************************************************
* Name : addF
* Purpose : adds fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : fraction (sum of f1 and f2)
******************************************************/
Fraction addF(Fraction f1, Fraction f2);
/*******************************************************
* Name : subF
* Purpose : substracts fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : fraction (substruction of f1 and f2)
******************************************************/
Fraction subF(Fraction f1, Fraction f2);
/*******************************************************
* Name : multF
* Purpose : multiplyes fractions
* Input : f1, f2 – two fractions
* Output : none
* Return : fraction (multiplication of f1 and f2)
*****************************************************/
Fraction multF(Fraction f1, Fraction f2);
/*******************************************************
* Name : divF
* Purpose : divedes fractions
* Input : f1, f2 – two fractions
* Output : none,
* wasErr() can be called after,
* to test whether the result is correct
* Return : fraction (division of f1 and f2)
******************************************************/
Fraction divF(Fraction f1, Fraction f2);
/*******************************************************
* Name : convertFromIntF
* Purpose : converts int type to fraction
* Input : num – number to convert
* Output : none
* Return : fraction
******************************************************/
Fraction convertFromIntF(int num);
/*******************************************************
* Name : convertToDoubleF
* Purpose : converts fraction to double type
* Input : f – fraction to convert
* Output : none
* Return : double type number
******************************************************/
double convertToDoubleF(Fraction f);
/*******************************************************
* Name : getNumeratorF
* Purpose : returns fraction numerator
* Input : f – fraction to convert
* Output : none
* Return : numerator
******************************************************/
int getNumeratorF(Fraction f);
/*******************************************************
* Name : getDenomimantor
* Purpose : returns fraction denominator
* Input : f – fraction to convert
* Output : none
* Return : denominator
******************************************************/
int getDenomimantor(Fraction f);
/*******************************************************
* Name : wasErr
* Purpose : test whether previous function
* returned correct result
* WORKS ONLY AFTER initF, divF
* Input : none
* Output : none
* Return : none
******************************************************/
int wasErr();
void printF(Fraction f);
----- файл fraction.h -----
#include "fraction.h"
int wasError;
Fraction initF(int num, int denum)
{
Fraction f;
if (denum > 0)
{
f.numerator = num;
f.denominator= denum;
shorten(&f);
wasError = 0;
} else
{
wasError = 1;
}
return f;
}
int isNullF(Fraction f)
{
if (f.numerator == 0)
{
return 1;
} else
{
return 0;
}
}
int compareF(Fraction f1, Fraction f2)
{
Fraction f;
f = subF(f1,f2);
if (f.numerator > 0)
{
return 1;
} else
{
if (f.numerator < 0)
{
return -1;
} else
{
return 0;
}
}
}
Fraction addF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = (f1.numerator*f2.denominator) +
(f2.numerator*f1.denominator);
res.denominator = f1.denominator*f2.denominator;
shorten(&res);
return res;
}
Fraction subF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = (f1.numerator*f2.denominator) -
(f2.numerator*f1.denominator);
res.denominator = f1.denominator*f2.denominator;
return res;
}
Fraction multF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = f1.numerator*f2.numerator;
res.denominator = f1.denominator*f2.denominator;
return res;
}
Fraction divF(Fraction f1, Fraction f2)
{
Fraction res;
res.numerator = f1.numerator*f2.denominator;
res.denominator = f1.denominator*f2.numerator;
if (res.denominator != 0)
{
wasError = 0;
} else
{
wasError = 1;
res.numerator = 0;
res.denominator = 1;
}
return res;
}
Fraction convertFromIntF(int num)
{
Fraction res;
res.numerator = num;
res.denominator = 1;
return res;
}
double convertToDoubleF(Fraction f)
{
return ((double)f.numerator / (double)f.denominator);
}
int getNumeratorF(Fraction f)
{
return f.numerator;
}
int getDenomimantor(Fraction f)
{
return f.denominator;
}
long gcd(long a, long b)
{
a = labs(a);
b = labs(b);
while ((a!=0) && (b!=0))
{
if (a > b)
{
a = a - b;
} else
{
b = b - a;
}
}
if (a!= 0)
{
return a;
} else
{
return b;
}
}
void shorten(Fraction *f)
{
int div;
div = (int)gcd((long)(*f).numerator,
(long)(*f).denominator);
(*f).numerator = (*f).numerator / div;
(*f).denominator = (*f).denominator / div;
}
void shortenLong(long num, long denum, long *resNum,
long *resDenum)
{
long div;
div = gcd(num, denum);
*resNum = num / div;
*resDenum = denum / div;
}
int wasErr()
{
return wasError;
}
void printF(Fraction f)
{
printf("%d/%d",f.numerator, f.denominator);
}
Пример использования:
/******************************************************
date: 10 January 2013
description: string reading and encoding sample
******************************************************/
#include <stdio.h>
#include "fraction.h"
int main()
{
Fraction f1, f2, f3;
f1 = initF(8,16);
f2 = initF(2,1);
f3 = addF(f1,f2);
printf("f1: ");
printF(f1);
printf("\n");
printf("f2: ");
printF(f2);
printf("\n");
printf("f3: ");
printF(f3);
printf("\n");
printf("f1 compare to f2: %d\n", compareF(f1,f2));
printf("To double: %f\n",convertToDoubleF(f3));
printf("Ended.");
return 0;
}
/* Output:
f1: 1/2
f2: 2/1
f3: 1/1
f1 compare to f2: -1
To double: 1.000000
Ended. */