Абстрактный тип данных
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. */