Опубликован: 05.11.2013 | Доступ: свободный | Студентов: 542 / 46 | Длительность: 11:51:00
Лекция 6:

Абстрактный тип данных

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. */