Назад | Вперед

Тема 5. «Простые» варианты сложных данных

Понятие структуры
Записи в языке Паскаль
Массивы
Новые типы данных
Множества

Понятие структуры

До сих пор рассматривались базовые типы данных: числа, строки, логи­ческие величины — и операции над базовыми данными. Однако для повышения производительности труда программистов и повышения качества их работы необхо­димо, чтобы язык программирования имел средства, позволяющие описывать данные в виде, максимально приближенном к их реальным аналогам. Например, чтобы орга­низовать обработку данных по студентам, в программе удобно не просто описать десяток различных переменных, а объединить их в структуру (или запись) «студент», состоящую из полей разного типа «имя», «пол», «год рождения», «группа» и т. д.
Современные языки программирования позволяют применять также сложные типы данных, составляющиеся из базовых и определенных ранее сложных типов. В резуль­тате удается организовывать структуры данных произвольной сложности: списки, деревья и т. п. При этом структура объединяет группу разных данных под одним названием (см. табл. 2.8).

Таблица 2.8
Синтаксис описания структуры


Бейсик

Паскаль

Си++

TYPE имя-струкгуры
поле AS тип

END TYPE

Record
поле: тип;

end;

struct имя
{
тип поле;

};

Получить доступ к отдельным составляющим (полям) этой структуры можно по их именам. В рассматриваемых языках программирования такой доступ осуществляется указанием имени структуры и имени поля через точку. Если подобным способом происходит обращение к полю, которое само является структурой, то выделение нужного поля продолжается приписыванием справа имени вложенного поля через точку.
Вот примеры описания структур.
Бейсик: 
TYPE Student
Name AS STRING
Sex AS INTEGER
BirthYear AS INTEGER
END TYPE
Паскаль:
record
Name: string;
Sex: Boolean;
BirthYear: integer;
end;
Си++:
struct Student
{
bool Sex;
int BirthYear;
Sex: Boolean;
};


Наверх

Записи в языке Паскаль

Как следует из таблицы 2.8, структура (или запись в языке Паскаль) представляет собой совокупность ограниченного  числа  логически связанных компонент,  принадлежащих к разным типам.  Компоненты записи называются полями, каждое из которых определяется именем. Поле записи содержит имя поля, вслед за которым через двоеточие указывается тип этого поля. Поля записи могут относиться к любому типу, допустимому в языке Паскаль, за исключением файлового типа.
Описание записи в языке ПАСКАЛЬ осуществляется с помощью служебного слова RECORD,  вслед за которым описываются компоненты записи. Завершается описание записи служебным словом END.
Например, записная книжка содержит фамилии,  инициалы и номера телефона, поэтому отдельную строку в записной книжке удобно представить в виде следующей записи:

type Row=Record
        FIO: String[20];
        TEL: String[7]
       end;
  var  str: Row;
Описание записей возможно и без использования имени типа,   например:
  var  str: Record
        FIO: String[20];
        TEL: String[7]; 
       end;
Обращение к записи в целом допускается только в операторах присваивания, где  слева и справа от знака присваивания используются  имена записей одинакового типа. Во всех остальных случаях оперируют отдельными полями записей.  Чтобы обратиться к отдельной компоненте записи, необходимо задать  имя записи и через точку указать имя нужного поля, например:
  str.FIO,   str.TEL
Такое имя называется составным. Компонентой записи может быть также запись, в таком случае составное имя будет содержать не два,  а большее количество имен.
Обращение к  компонентам записей можно упростить,  если воспользоваться оператором присоединения with.
Он позволяет заменить составные имена,  характеризующие каждое поле, просто на имена полей, а имя записи определить в операторе присоединения:
  with M do OP;
Здесь М  -  имя  записи,   ОР  - оператор,  простой или составной.
Оператор ОР представляет собой область действия оператора присоединения, в пределах которой можно не использовать составные имена.
Иногда содержимое отдельной записи зависит от значения  одного  из ее полей. В языке ПАСКАЛЬ допускается описание записи,  состоящей из общей и вариантной частей. Вариантная часть задается с помощью конструкции
  case P of,
где  Р - имя  поля из общей  части  записи.
Возможные значения, принимаемые этим полем,  перечисляются так же, как и в операторе варианта.
Однако вместо указания выполняемого действия, как это делается в операторе варианта,    указываются поля варианта,  заключенные в круглые скобки. Описание вариантной части завершается служебным словом end.
Тип поля Р можно указать в заголовке вариантной части, например:
  case P: Integer of
Инициализация записей  осуществляется  с  помощью   типизированных констант:
type
  RecType= Record
   x,y: Word;
  ch: Char;
  dim: Array[1..3] of Byte
end;
const
  Rec: RecType= ( x: 127; y: 255;
  ch: 'A';
  dim: (2, 4, 8) );


Наверх

Массивы 

Доступ к элементам  структуры осуществляется по имени ее составляющих. В одних случаях это значительно повышает наглядность исходных текстов и упрощяет процесс программирования, но имеется немало ситуаций, когда надо организовать обработку больших объемов данных одного типа, при этом создавать структуры с сотнями и тысячами полей неразумно. Поэтому в дополнение к структурам в языки программирования введено понятие массива, сложного типа данных, доступ к элементам которого происходит по их положению, по номеру или индексу. Например, можно описать массив, состоящий из тысячи элеменов численного типа, и затем обратится к десятому или сотому элементу по его номеру.
При описании массива обычно указывается его размер (число элементов) или верхняя и нижняя граница массива. Поэтому синтаксис описания массивов в разных языках примерно одинаков (см. табл. 2.9).
Таблица 2.9


Язык

Синтаксис описания массивов

Пример

Бейсик

DIM имя(число элементов) AS тип

DIM IntArray(1000) AS INTEGER

Паскаль

array[нижняя граница…верхняя граница] of тип

array[1..1000] of integer;

С++

тип имя[число элементов 1000].

int IntArray[1000];

Итак, массивы представляют собой ограниченную упорядоченную совокупность однотипных величин. Каждая отдельная величина называется компонентой массива. При этом тип компонент может быть любым базовым, принятым в языках высокого уровня (в ПАСКАЛе, например все кроме файлового типа), а вся совокупность компонент определяется одним именем.
Например, в Паскале, для обозначения отдельных компонент используется конструкция, называемая переменной с индексом или с индексами:
A[5]     S[k+1]     B[3,5].
В качестве индекса может быть использовано выражение. Тип индексов может быть только интервальным или перечисляемым.   Действительный  и целый типы недопустимы. Индексы интервального типа, для которого базовым является целый тип,  могут принимать отрицательные,  нулевое  и положительные значения.{}
В операторной части программы один массив может быть присвоен другому, если их типы идентичны, например:
   R1:=Z.
Для ввода  или вывода массива в список ввода или вывода помещается переменная с индексом,  а операторы ввода или  вывода  выполняются  в цикле.
Первый индекс  определяет  номер  строки,  второй - номер столбца. Двумерные массивы хранятся в памяти ЭВМ по строкам.
Инициализация массивов (присвоение начальных значений всем  компонентам массивов) осуществляется двумя способами.
Первый способ - с использованием типизированных констант, например:
type Dim10= Array[1..10] of Real;
const
  raM10: Dim10 = (0,2.1,4,5.65,6.1,6.7,7.2,8,8.7,9.3);
При инициализации двумерных массивов значения компонент каждого из входящих в него одномерных массивов записывается в скобках:
type
  Dim3x2= Array[1..3,1..2] of Integer;
Const
  aM3x2: Dim3x2= ( (1, 2)(3, 4)(5, 6) );
Второй способ инициализации - использование разновидности процедуры FillChar:
  FillChar( var V; NBytes: Word; B: Byte );
Эта процедура заполняет участок памяти однобайтовым значением. Например, для обнуления массива A[1..10] of Real можно записать:
  FillChar(A, 40, 0);
или
  FillChar(A, SizeOf(A), 0);
Массивы, границы которых явно заданы в команде описания, называются стати­ческими. Их размер известен заранее и не меняется на всем протяжении работы программы.
В последних версиях компилируемых языков программирования реализуются так называемые динамические массивы, размер которых может меняться во время выпол­нения программы. В ряде случаев это весьма удобно, так как позволяет экономно расходовать память, захватывая ее по мере необходимости. Недостаток динами­ческих массивов в том, что организовать эффективную работу с ними, используя компиляторы, сложно. Приходится выполнять множество проверок, связанных с расходованием памяти компьютера, что понижает общую эффективность приложения. Динамические массивы в Паскале начали поддерживаться совсем недавно, с активным распространением новых мощных ПК, а в интерпретируемых языках типа Бейсика это было сделано довольно давно.
Во многих языках программирования строки рассматриваются как массивы сим­волов, которые допускается индексировать как обычные массивы.
Так, стандартный ПАСКАЛЬ допускает два способа хранения символьных массивов в памяти ЭВМ: распакованный и упакованный. Распакованные массивы символов хранятся в памяти ЭВМ по одному символу в машинном слове, упакованные по одному символу в байте. При описании упакованного массива символов используют служебное слово PACKED, например:
  var   MAS: Packed Array[1..20] of Char;
Описание распакованного массива символов имеет вид:
  var   M: Array[1..20] of char;
Для преобразования символьного массива из  распакованной  формы  в упакованную и наоборот,  из упакованной в распакованную,  в язык ПАСКАЛЬ введены две стандартные функции Pack, UnPack.
Упакованный массив символов образует символьную строку. Символьная строка может быть либо строковой константой, либо строковой  переменной. Строковая константа, или строка, представляет собой совокупность символов, заключенную  в апострофы.  Строка - это элементарная  конструкция языка ПАСКАЛЬ. Строковые константы могут входить в состав выражений. Как  и числовые константы,  они могут быть описаны в разделе описания констант.
Строковые переменные - это одномерные упакованные  массивы  символов, для описания которых в TURBO PASCAL введен тип String. Например, если строка содержит до 30 символов,  ее тип будет определен как  
  type   s= String[30];
Длина строки не может содержать более, чем 255 символов.
В TURBO PASCAL определено понятие строки переменной длины,  в этом случае ее описание задается как
  type  s= String;
Тип String без указания длины совместим со всеми типами строк.
Особенностью строковых переменных является то, что к ним можно обращаться как к скалярным переменным, так и к массивам. Во втором случае применяется конструкция "переменная с индексом", что обеспечивает доступ к   отдельным символам строки.  При этом нижняя граница идекса равна 1. Отдельный символ строки совместим с типом Char.
В памяти ЭВМ строка занимает количество байтов, на единицу большее ее длины. Нулевой байт строки содержит ее длину.
Для строк определены операции присваивания, слияния (конкатенации) и сравнения.
Для сравнения строк применяются все операции отношения.  Сравнение строк происходит посимвольно,  начиная с первого символа. Строки равны, если имеют одинаковую длину и посимвольно эквивалентны.
Строки могут быть элементами списка ввода - вывода, при этом записывается имя строки без индекса.
При вводе  строковых переменных количество вводимых символов может быть меньше,  чем длина строки. В этом случае вводимые символы размещаются с начала строки, а оставшиеся байты заполняются пробелами. Если количество  вводимых  символов  превышает  длину  строки,   лишние символы отбрасываются.
Инициализация строк может производиться как с помощью типизированных констант:
  const sName: String[9]= 'IBM PC/AT';
так и с использованием второй разновидности функции FillChar:
  FillChar( var V; NBytes: Word; C: Char );
например:
  FillChar(A, SizeOf(A), '0');
Для работы  со  строками в TURBO PASCAL включены процедуры и функции, которые обеспечивают редактирование и преобразование строк.

Правила работы со сложными типами

Отличие базовых типов от сложных в том, что в базовых типах нельзя выделить составные части. При этом поле структуры или элемент массива считаются обыч­ными переменными, и их использование в любых операторах ничем не отличается от использования переменных базовых типов.
В развитых языках программирования допускаются массивы, состоящие из структур, и структуры, состоящие из массивов. При этом возможны достаточно сложные формы записи, например:
а[0].Items.Strings[4].value
Массив а состоит из структур, в описании которых есть поле Items, являющееся тоже структурой, имеющей поле Strings, которое, в свою очередь, представляет собой массив структур, имеющих поле value.


Наверх

Новые типы данных

При определении нескольких переменных со сложной структурой удобно описывать каждую переменную, многократно используя одну и ту же запись структуры. Если, например, в нее потребуется внести изменение (добавить новое поле, изменить тип существующего и т. д.), то придется делать это несколько раз, рискуя ошибиться и пропустить одно из описаний, особенно если они сделаны в разных местах про­граммы.
Чтобы избежать этой проблемы и позволить программистам активно применять нужные структуры данных, в современных языках программирования разрешено определять собственные типы данных, которые допускается использовать в коман­дах описания наравне с базовыми типами.
Синтаксис описания нового типа данных


Бейсик

Паскаль

Си++

Аналогичен описанию
структуры, которое уже
является описанием нового типа

type имя = описание;

typedef struct имя-структуры
{
поля-структуры;
} имя;
Имя структуры надо указывать только из-за требований синтаксиса. Реально оно нигде не применяется.

Название нового типа можно использовать во всех последующих командах описания переменных.
Паскаль:
type TMyArray = array[0..99] of integer;
type TMyRecord = record
    Iteml: integer;
    Item2: string;
end;
var MyArray: TMyArray;
var R: TMyRecord;

Си++:
  __typedef struct name
  {
  int i;
  float x;
  } TNewStruct;
  TNewStruct NewStruct
  }
Так, например, может быть определен так называемый перечисляемый тип данных, который представляет  собой  ограниченную  упорядоченную последовательность скалярных констант заданного типа. Значение каждой константы задается, обычно, ее именем.  Имена отдельных  констант отделяются друг от друга запятыми,  а вся совокупность констант, составляющих данный перечисляемый тип, заключается в круглые скобки.
Программист объединяет в одну группу в соответствии с каким - либо признаком всю совокупность значений,  составляющих перечисляемый тип.
Например, перечисляемый    тип  Rainbow (РАДУГА)  объединяет скалярные значения RED, ORANGE, YELLOW, GREEN, LIGHT_BLUE, BLUE, VIOLET (КРАСНЫЙ, ОРАНЖЕВЫЙ,  ЖЕЛТЫЙ, ЗЕЛЕНЫЙ, ГОЛУБОЙ, СИНИЙ, ФИОЛЕТОВЫЙ). Перечисляемый тип Traffic_Light (СВЕТОФОР) объединяет скалярные значения RED, YELLOW, GREEN (КРАСНЫЙ,  ЖЕЛТЫЙ, ЗЕЛЕНЫЙ).
Перечисляемый тип  описывается  в разделе описания типов,  который начинается со служебного слова type, например:
type
  Rainbow = (RED, ORANGE, YELLOW, GREEN, LIGHT_BLUE, BLUE, VIOLET);
Каждое значение  является константой своего типа и может принадлежать только одному из перечисляемых типов, заданных в программе. Например, перечисляемый    тип  Traffic_Light не может быть определен в одной программе с типом Rainbow,  так как оба типа содержат одинаковые константы.
Описание переменных, принадлежащих к скалярным типам, которые объявлены в  разделе описания типов,  производится с помощью имен типов.
Например:
  type  Traffic_Light= (RED, YELLOW, GREEN);
  var   Section: Traffic_Light;  
Это означает, что переменная Section может принимать значения RED, YELLOW или GREEN.
Переменные перечисляемого типа могут быть описаны в разделе описания переменных, например:
  var  Section: (RED, YELLOW, GREEN);
При этом имена типов отсутствуют,  а переменные определяются совокупностью значений, составляющих данный перечисляемый тип.
К переменным перечисляемого  типа  может  быть  применим  оператор присваивания:
  Section:= YELLOW;
Упорядоченная последовательность значений, составляющих перечисляемый тип, автоматически нумеруется, начиная с нуля и далее через единицу. Отсюда следует, что к перечисляемым переменным и константам могут быть применены операции отношения  и  стандартные  функции  Pred, Succ, Ord.
Переменные и константы перечисляемого типа не могут быть элементами списка ввода или вывода.
Аналогично может быть определен и так называемый интервальный тип данных
Отрезок любого порядкового типа может быть определен как интервальный или ограниченный тип. Отрезок задается диапазоном от минимального до максимального значения констант, разделенных двумя точками. В качестве констант могут быть использованы константы, принадлежащие к целому, символьному, логическому или перечисляемому типам. Скалярный тип, на котором строится отрезок, называется базовым типом.
Минимальное и максимальное  значения констант называются нижней и верхней границами отрезка, определяющего интервальный тип. Нижняя граница должна быть меньше верхней.
Над переменными,  относящимися к интервальному типу,  могут выполняться все операции и применяться все стандартные  функции,   которые допустимы для соответствующего базового типа.
При использовании в программах интервальных типов данных может  осуществляться контроль над тем, чтобы значения переменных не выходили за границы, введенные для этих переменных в описании интервального типа.


Наверх

Множества 

Понятие множества в языке ПАСКАЛЬ основывается  на  математическом представлении о  множествах:  это ограниченная совокупность различных элементов. Для построения конкретного множественного типа используется перечисляемый или интервальный тип данных.  Тип элементов, составляющих множество, называется базовым типом.
Множественный тип описывается с помощью служебных слов Set of, например:
  type  M= Set of B;
Здесь М - множественный тип, В - базовый тип.
Пример описания переменной множественного типа:
type
  M= Set of 'A'..'D';
var
  MS: M;
Принадлежность переменных к множественному типу может быть определена прямо в разделе описания переменных:
var
  C: Set of 0..7;
Константы множественного  типа  записываются  в виде заключенной в квадратные скобки последовательности элементов или интервалов базового типа, разделенных запятыми, например:
  ['A', 'C']    [0, 2, 7]    [3, 7, 11..14].
Константа вида  [ ] означает пустое подмножество.
Множество включает в себя набор элементов базового типа, все подмножества данного множества, а также пустое подмножество. Если базовый тип, на котором строится множество, имеет К элементов, то число подмножеств, входящих в это множество, равно 2 в степени К. Пусть имеется переменная Р интервального типа:
var P: 1..3;
Эта переменная может принимать три различных значения  -  либо  1, либо 2, либо 3. Переменная Т множественного типа
  var T: Set of 1..3;
может принимать восемь различных значений:
  [ ]         [1,2]
  [1]        [1,3]
  [2]        [2,3]
  [3]        [1,2,3]
Порядок перечисления элементов базового типа в константах  безразличен.
Значение переменной  множественного  типа  может быть задано конструкцией вида [T], где T - переменная базового типа.
К переменным и константам множественного типа  применимы  операции присваивания (:=), объединения (+), пересечения (*) и вычитания (-):
  ['A','B'] + ['A','D']      даст  ['A','B','D']
  ['A'] * ['A','B','C']      даст  ['A']
  ['A','B','C'] - ['A','B']  даст  ['C'].
Результат выполнения  этих  операций  есть величина множественного типа.
К множественным величинам применимы операции: тождественность (=), нетождественность (<>), содержится  в (<=), содержит (>=).  Результат выполнения этих операций имеет логический тип, например:
  ['A','B'] = ['A','C']  даст FALSE
  ['A','B'] <> ['A','C'] даст TRUE
  ['B'] <= ['B','C']     даст TRUE
  ['C','D'] >= ['A']     даст FALSE.
Кроме этих операций для работы с величинами множественного типа  в языке ПАСКАЛЬ используется операция    in,  проверяющая  принадлежность  элемента  базового типа,  стоящего слева от знака операции,  множеству, стоящему справа от знака операции. Результат выполнения этой операции - булевский.  Операция проверки принадлежности элемента множеству часто используется вместо операций отношения, например:
  A in ['A', 'B'] даст  TRUE,
  2 in [1, 3, 6]  даст  FALSE.
При использовании  в   программах   данных   множественного   типа выполнение операций происходит над битовыми строками данных.  Каждому значению множественного типа в памяти ЭВМ соответствует один двоичный разряд. Например, множество
  ['A','B','C','D']
представлено в памяти ЭВМ битовой строкой
  1 1 1 1.
Подмножества этого множества представлены строками:
  ['A','B','D']   1 1 0 1
  ['B','C']       0 1 1 0
  ['D']           0 0 0 1
Величины  множественного типа не могут быть элементами списка ввода - вывода.
В каждой  конкретной  реализации транслятора с языка ПАСКАЛЬ количество элементов базового типа,  на котором строится множество, ограничено. В  TURBO PASCAL количество базовых элементов не должно превышать 256.
Инициализация величин  множественного  типа производится с помощью типизированных констант:
  const  seLit: Set of 'A'..'D'= [];
Проиллюстрируем применение  данных множественного типа на примере.
Пример. Составить программу, которая вырабатывает и выводит на экран дисплея наборы случайных чисел для игры в "Спортлото 5 из 36".
Для заполнения каждой карточки спортлото необходимо получить набор из пяти псевдослучайных чисел. К этим числам предъявляются два требования:
-числа должны находиться в диапазоне 1..36;
-числа не должны повторяться.

 Program Lotto; 
  var 
    nb, k: Set of 1..36; 
    kol, l, i, n: Integer; 
  begin 
   Randomize; 
   WriteLn('ВВЕДИ kol'); 
   ReadLn(kol); 
   nb:=[1..36]; 
   for i:=1 to kol do 
   begin 
    k:=[]; 
    for l:=1 to 5 do 
    begin 
     repeat 
      n:=Random(36) 
     until (n in nb) and not (n in  k); 
    k:=k+[n]; 
    Write(n:4) 
    end; 
    WriteLn 
   end 
  end.


Наверх

Назад Оглавление Вперед