Г л а в а 4

ТИПЫ ДАННЫХ

       
  4.1 Типы данных 
  4.2 Преобразование типов и действия над ними
  4.3 Простые типы
    4.3.1 Порядковые типы
    4.3.2 Вещественные типы
  4.4 Структурированные типы
    4.4.1 Массивы 
    4.4.2 Сортировка массивов 
    4.4.3 Записи
    4.4.4 Множества
  4.5 Строки
  4.6 Совместимость и преобразование типов
Тесты на закрепление материала

оглавление


   Любые данные, т.е. константы, переменные, значения функций или выражения, в Турбо Паскале характеризуются своими типами. Тип определяет множество допустимых значений, которые может иметь тот или иной объект, а также множество допустимых операций, которые применимы к нему. Кроме того, тип определяет также и формат внутреннего представления данных в памяти ПК.
   Турбо Паскаль характеризуется разветвленной структурой типов данных (рис.6).

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

4.1. ТИПЫ ДАННЫХ


   Структура рассмотренной программы имеет следующий вид:
PROGRAM MyFlrstProgram;
{ Раздел описаний ) 
BEGIN
{ Раздел операторов } 
END.
   Слова PROGRAM, BEGIN и END выделяют две части программы - раздел описаний и раздел операторов (именно по этому эти слова всюду в текстах программ и примерах в книге выделены прописными буквами).
   Такая структура обязательна для любой программы, что следствием жесткого требования языка: любой нестандартный для языка Турбо Паскаль идентификатор, используемый в исполняемых операторах, должен быть предварительно описан в разделе описаний. (Стандартные идентификаторы связаны с предварительно объявленными объектами и входят в стандартную библиотеку Турбо Паскаля. Таким, является идентификатор WR1TELN. Стандартные идентификаторы, если они используются в программе, описывать не нужно).
   По сравнению с языком Фортран требование предварительного описания идентификаторов кажется чрезмерно строгим и делающим менее свободным. На самом деле в нем проявляется тенденция развил языков программирования в сторону повышения надежности создаваемой программы. Кто программировал на языке Фортран, знает, как бывает трудно обнаружить в большой программе ошибочно введенный или пропущенный символ в идентификаторе. Если, например, всюду в программе используется переменная с именем EPSILON, а в одном месте ошибочно написано EPSLON, то программа может благополучно откомпилироваться и даже давать почти правдоподобный результат для некоторых наборов данных, но в какой-то момент начнет вести себя стран. Обязательное предварительное описание идентификаторов в Турбо Паскале защищает программы от такого рода ошибок и повышает их надёжность.
   Описать идентификатор - это значит указать тип связанного с ним объекта программы (константы или переменной). Понятие типа - одно фундаментальных понятий Турбо Паскаля. В гл.4 подробно рассмотрены различные типы, а здесь лишь укажем, что тип определяет, во-первых, способ внутреннего представления объекта и, во-вторых, действия, которые разрешается над ним выполнять.
   На первых порах понадобятся следующие простые типы:
INTEGER - целочисленные данные, во внутреннем представлен занимают 2 байта; диапазон возможных значений - от -32768 до +32767 данные представляются точно;
REAL - вещественные данные, занимают б байт; диапазон возможных значений модуля - от 2.9Е-39 до 1.7Е+38; точность представления данных - 11... 12 значащих цифр;
CHAR - символ, занимает 1 байт;
STRING - строка символов, занимает МАХ+1 байт, где МАХ - максимальное число символов в строке;
BOOLEAN - логический тип, занимает 1 байт и имеет два значения: FALSE (ложь) и TRUE (истина).
   Тип константы определяется способом записи ее значения. Например:


const 
С1=17; 
C2=3.14; 
CЗ='А';
C5=false;


   При анализе этого фрагмента программы компилятор отнесет первую константу к типу INTEGER, вторую - к типу REAL, третью - к CHAR, четвертую - к STRING и последнюю – к BOOLEAN. Признаком, позволяющим отнести константу к REAL или к INTEGER, является наличие или отсутствие десятичной точки в ее значении. Разумеется, константы С2 и С4 относятся к разным типам: С2 - к REAL (в константе есть десятичная точка), а С4 - к STRING (константа обрамлена апострофами). Константу C3 компилятор будет считать относящейся к типу CHAR: одиночный символ в апострофах относится к CHAR, в то время как несколько символов к STRING.
   В отличие от константы переменная именует объект программы, который может изменять свое значение в ходе счета. При описании переменных за идентификатором ставятся двоеточие и имя типа. Несколько однотипных переменных можно объединять в список, разделяя их запятыми. В начале раздела описания переменных должно стоять зарезервированное слово VAR (VARiables - переменные). Например:

Var
Sigma: real;
a, b, c, d: char;
textl: string (15);
text2: string;
flag: Boolean;.


   Как уже говорилось, тип данных определяет длину внутреннего представления соответствующих переменных. В частности, длина внутреннего представления переменных типа STRING (строка символов) зависит от максимального числа символов, которые могут составлять строку. В приведенном выше примере переменная TEXTI описана с указанием ее максимальной длины (15 символов), а в описании переменной ТЕХТ2 максимальная длина не указана и компилятор установит для нее предельно допустимую в Турбо Паскале длину - 255 символов.
   Рассмотрим еще одну несложную программу (пример 2). Ее назначение: ввести с клавиатуры два целых числа, найти отношение первого числа ко второму и вывести полученный результат на экран. Пример 2.
{Программа вводит два целых числа и выводит частное от деления 1-го на 2-е } 
var
N1, n2: Integer; {n1 и n2 - вводимые целые)
х : real; { х - результат }
BEGIN
wrlte('n1='); {Сообщить о вводе п1}
readln(nl); {Ввести л1}
wrlte(‘n2=‘); {Сообщить о вводе п2}
readln(n2); {Ввести п2 }
x:=n1/n2; {Найти результат }
wrlteln('n1/n2=’,x); {Вывести его }
END.

Прежде всего бросается в глаза появление в программе поясняющих комментариев. Комментарий в Турбо Паскале - это произвольная после любых символов, обрамленная фигурными скобками. Комментарий разрешается вставлять в любое место программы, где по смысл может стоять пробел. В качестве ограничителей комментария допускаете использование фигурных скобок «{» и «}», а также пары символов: «(*» слева от комментария и «*)» - справа от него:
                                                         { Это - комментарий } 
                                                    (* Это - тоже комментарий *)
   Комментарии с однотипными ограничителями нельзя вкладывать друг в друга, т.е. недопустимы последовательности вида
                                                  { ... { ... } ... }или (* ... (* ... *) ... *) 
   Однако можно вкладывать комментарии с ограничителями разны типов (не более одной глубины вложения):
                                                  { ... (* ... *) ... }или (* ... { ... } … *)
   Последнее обстоятельство проясняет кажущуюся странной избыточность ограничителей: если всюду в программе будут использоваться ограничители одного типа, то для того, чтобы временно исключить из программы какой-либо фрагмент текста, достаточно заключить его в ограничите ли другого типа.
   Наличие комментариев в программе избавляет меня от необходимости пояснять назначение отдельных строк программы. Несколько слов о вводе данных. Пары операторов
                                                                   write (..);
                                                                  readln(..);
работают следующим образом. Вначале оператор WRITE выводит на экран и оставляет курсор в конце только что выведенной текста. Заметим, что оператор
                                                                wrlteln(text);
в примере 1 после вывода текста осуществлял перевод строки и устанавливал курсор в начало следующей строки, экрана. Именно в этом простом действии (переводе строки) заключается единственное отличие в процедуры WRITELN от процедуры WRITE.
   Затем по оператору READLN вызывается встроенная процедура да данных и программа останавливается в ожидании ввода. В этот момент необходимо набрать на клавиатуре нужное число и нажать клавишу «Ввод». Сразу после этого программа продолжит работу: проанализировав введенное число и перейдет к вводу следующего числа или вычислению результата. Таким образом, сигналом окончания подготовки очередного числа является нажатие на клавишу «Ввод», до этого момента можно стирать любой ошибочно введенный символ клавишей «Забой».
   Для вычисления отношения введенных чисел используется один из основных операторов Турбо Паскаля - оператор присваивания. В его левой части указывается имя переменной, правая часть представляет собой выражение того же типа, что и переменная. Пара символов «:=», связывающая левую и правую части оператора присваивания, означает «присвоить значение». Запомним: в операторах присваивания Турбо Паскаль всегда используются символы<:=>, в то время как при описании констант -одиночный символ «-». С точки зрения синтаксиса языка, два символа<:=> рассматриваются как один специальный символ и обязательно пишутся слитно.
   Оператор присваивания используются практически во всех языках программирования. В некоторых языках, например в Фортране или Бейсике , символ присваивания является знак равенства, однако новичка, привыкшего к строгости математических формул, может озадачить типичная форма записи Фортран- оператора присваивания, например, такая:
X=x+1 
Вариант записи на Турбо Паскале
X:=x+1
в этом смысле кажется более логичным. Разумеется, вряд ли кому-нибудь придет в голову видеть уравнения там, где их нет. Конечно же, и в том, и в том, и другом случае реализуется одно и то же алгоритмическое действие: к содержимому X прибавляется 1 и полученный результат вновь присваивается переменной X. Обратите внимание на оператор вывода результатов

writeln (‘n1/n2=’,x);

   В нём в качестве одного из параметров явно указывается константа типа строка символов ' П 1 / П 2. 'Конечно же, константы (в отличие от временных) вовсе не обязательно описывать в разделе описаний, так как тип легко определяется компилятором по форме записи константы. С отчетом этого можно было бы записать программу из примера 1 предельно конечно:
BEGIN
  writeln('Я программирую на Турбо Паскале');
END.

                 Вверх

                                       

4.2. ПРЕОБРАЗОВАНИЯ ТИПОВ И ДЕЙСТВИЯ НАД НИМИ


   Как уже говорилось, тип переменной позволяет не только устанавливать длину ее внутреннего представления, но и контролировать те действия, которые выполняются над ней в программе. Контроль за использованием переменных еще на этапе компиляции программы - важное преимущество Турбо Паскаля перед другими языками программирования, в которых допускается автоматическое преобразование типов. В Турбо Паскале почти невозможны неявные (автоматические) преобразования типов. Исключение сделано только в отношении констант и переменных типа INTEGER (целые), которые разрешается использовать в выражениях типа REAL (вещественные). Если, например, переменные X и У описаны следующим образом:
var
  х : Integer; 
  у : real:
то оператор
  у :=х . 2:
будет синтаксически правильным, хотя справа от знака присваивания стоит целочисленное выражение, а слева - вещественная переменная компилятор сделает необходимые преобразования автоматически. В то время оператор
                                                                        х:= 2.0;
будет неверным, так как автоматическое преобразование типа REAL (константа 2.0 содержит десятичную точку и, следовательно, принадлежит к типу REAL) в тип INTEGER в Турбо Паскале запрещено.
   Разумеется, запрет на автоматическое преобразование типов еще и означает, что в Турбо Паскале нет средств преобразования данных. Они конечно же, есть, но их нужно использовать явно (подробнее об этом cм гл.4). Для преобразования данных в языке существуют встроенные функции, которые получают в качестве параметра значение одного типа, возвращают результат в виде значения другого типа. В частности, для преобразования REAL в INTEGER имеются даже две встроенные функции такого рода: ROUND округляет REAL до ближайшего целого, TRUNC усекает REAL путем отбрасывания дробной части.
   Например, ошибочным будет оператор
х := у/х; 
но правильным
х :=round(y/x): (объявления переменных см. выше).

   Понятие функции в Турбо Паскале близко к понятию процедур) Как и процедура, функция вызывается своим именем и может содержа: произвольное число операторов Турбо Паскаля и даже внутренних процедур и функций. Существенным отличием функции от процедуры является то обстоятельство, что функция имеет собственное значение и, следовательно, может использоваться наравне с переменными в выражений соответствующего типа.
   Для преобразования данных типа CHAR (символ) в целое число предназначена функция ORD, обратное преобразование INTEGER CHAR осуществляет функция CHR.
   С помощью несложной программы (пример 3) Вы сможете узнать внутренний код произвольного символа. Пример:.
{ Программа читает символ с клавиатуры и выводит на экран этот символ и соответствующий ему внутренний код }
var
  clt : char; { В эту переменную читается символ }
BEGIN
  wrlte('Введите любой символ: ');
  readln(ch); { Читать один символ );
  writeln(ch, '=' .ord(ch); { Преобразовать его к целому и вывести на экран }
END.
Обратите внимание: при вызове
writeln(ch, '=', ord(ch));
третьим параметром обращения указан вызов функции ORD(CH), что с гонки зрения языка является выражением; как мы увидим дальше (см. гл.8), во многих случаях при вызове процедур и функций в качестве параметров вызова можно указывать не только переменные или константы, но и выражения с их участием.
   По мере надобности мы будем знакомиться с другими функциями преобразования типов данных, а сейчас - о тех операциях, которые разрешены над различными типами.
   Конечно же, в Турбо Паскале есть все четыре арифметические операции над переменными REAL и INTEGER: + - сложение; - вычитание; * - умножение; / - деление вещественное; div - деление целочисленное.
   Наличие двух операций деления есть еще одно проявление основополагающего принципа Турбо Паскаля: программист должен явно подтверждать компилятору, что он готов к возможным последствиям преобразования типов. Если, например, в языке Фортран используется выражение 1/2 , то результат этого выражения будет зависеть от того, переменной какого типа он будет присвоен: если N есть переменная целого типа, а X - вещественного, то в программе на Фортране присваивания
N = Ѕ
X = 1/2
дадут значения 0 для N и 0.5 для X. В Турбо Паскале такой двусмысленности нет: выражение 1/2 всегда имеет значение 0.5 и поэтому оператор
var
  N : Integer:
  N := 1/2; 
просто недопустим. В то же время допустимый в Турбо Паскале оператор
var
  X : real;
  X := 1 dlv 2;
самим фактом использования операции целочисленного деления D1V свидетельствует о том, что программист сознательно отбрасывает дробную часть результата. (Надеюсь, что читатель извинит явную искусственность этих примеров, которая вызвана лишь стремлением проиллюстрировать обсуждаемые особенности языка).
   Для данных типа INTEGER в Турбо Паскале есть еще одна операция деления - MOD, т.е. получение остатка от целочисленного деления. Например:
5mod 2=1
31 mod 16=15
18 mod 3=0
   В Турбо Паскале отсутствует операция возведения в степень, что очевидно, будет вызывать определенные неудобства при реализации м числительных алгоритмов. Некоторым утешением может служить шип чие встроенной функции SQR, возвращающей квадрат от значения параметра, причем тип результата определяется типом параметра. (Читателям, имеющим опыт программирования на Бейсике, следует учесть, 41 функция SQR(X) в Турбо Паскале имеет прямо противоположный математический смысл по сравнению с одноименной функцией в Бейсике, которая извлекает корень квадратный из параметра X).
   И еще об одном существенном недостатке Турбо Паскаля: в отсутствуют комплексный тип и соответствующие операции над ними. Вообще, в отношении реализации разнообразных вычислительных процедур Турбо Паскаль значительно уступает Фортрану ОС ЕС. В частности, в нем намного беднее набор встроенных математических функций (см. гл. 4).
   При работе с целыми числами могут оказаться полезными две процедуры (здесь и далее в квадратных скобках указываются необязательны параметры):
DEC(X [,N]) - уменьшает содержимое переменной X на значение выражения N (если N не задано, то на 1); тип переменной X и выражение N - INTEGER (точнее, любой целый, см. гл. 4);
INC(X [,N]) - увеличивает значение X на N (если N не задано, то на 1).
   Над символами и строками символов определена единственная опeрация - сцепление двух строк. Операция обозначается символом «+» Например, программа
var
  st:string; 
BEGIN
  st:='Турбо' + '-‘ + 'Паскаль ':
  writeIn(11): 
END. 
напечатает строку
Турбо-Паскаль
Все остальные действия над строками и символами реализуются c помощью встроенных процедур и функций (см. гл.4).
   И, наконец, об операциях отношения и логических операциях. 
   Над данными типа REAL, INTEGER, CHAR, STRING определены
следующие операции отношения (сравнения):
= - равно;
<> - не равно;
< - меньше;
> -больше;
<= - меньше или равно,
>= - больше или равно.
  В операциях сравнения должны участвовать однотипные операнды.
  Исключение сделано опять-таки в отношении REAL и INTEGER, которые могут сравниваться друг с другом. Результат применения операции отношения к любым операндам имеет тип BOOLEAN.
Сравнение двух строк осуществляется следующим образом. Символы строк сравниваются попарно друг с другом так, что первый символ первой строки сравнивается с первым символом второй строки, второй символ первой строки - со вторым символом второй и т.д. Символы сравниваются путем сравнения их кодов во внутреннем представлении (см. гл. 4 и прил. 2). Если одна строка короче другой, недостающие символы заменяются нулем. Отношение первой несовпадающей друг с другом пары символов и принимается за отношение двух строк.
   При сравнении данных типа BOOLEAN учитывается внутреннее соглашение Турбо Паскаля, в соответствии с которым FALSE есть нулевой байт, a TRUE- байт с единицей в младшем разряде. Заметим, что функция ORD преобразует к целому не только символы, но и логические величины, поэтому
ord(false)-0, ord(true) -1. 
   В Турбо Паскале определены следующие логические операции:
not - логическое НЕ; or - логическое ИЛИ;
and - логическое И; хоr - исключительное ИЛИ.
   Логические операции применимы к операндам целого и логического типов. Если операнды - целые числа, то результат логической операции есть тоже целое число (подробнее об этом сказано в гл.4). Логические операции над логическими данными дают результат логического типа.
   При вычислении выражений любого типа приоритет вычислений определяется расставленными скобками, а при их отсутствии - по табл. 1 (в порядке убывания приоритета).
Таблица 1.

Приоритет операций

Приоритет

Операция

1 Not, @
2 *, /, div, mod, and, shl, shr
3 +, -, or, xor
4 =, <>, <, >, <=, >=, in


   Примечание. Операции @ (получение адреса объекта), in (принадлежность к множеству), shl и shr (сдвиги операндов) описаны в гл.4.
   Следует учесть, что в отличие от многих других языков программирования в Турбо Паскале логические операции имеют более высокий приоритет, чем операции отношения. В связи с этим, в сложных логических выражениях обычно необходимо расставлять скобки. Если, например, бис имеют тип INTEGER , то выражение

A= b and c<d 
вызовет сообщение о синтаксической ошибке, так как сначала выполнится операция b and c. Правильным будет выражение:

(a = b) and ( c < d )

                       Вверх

4.3. ПРОСТЫЕ ТИПЫ


   К простым типам относятся порядковые и вещественные типы.
Порядковые типы отличаются тем, что каждый из них имеет конечное число возможных значений. Эти значения можно определенным образом упорядочить (отсюда - название типов) и, следовательно, с каждым из них можно сопоставить некоторое целое число - порядковый номер значения.
   Вещественные типы, строго говоря, тоже имеют конечное число значений, которое определяется форматом внутреннего представления вещественного числа. Однако количество возможных значений вещественных типов настолько велико, что сопоставить с каждым из них целое число (его номер) не представляется возможным.
 
                    Вверх

4.3.1. Порядковые типы


   К порядковым типам относятся (см. рис.6) целые, логический, символьный, перечисляемый и тип-диапазон. К любому из них применима функция ORD(X), которая возвращает порядковый номер значения выражения X. Для целых типов функция ORD(X) возвращает само значение X, т.е. ОRD(Х)-Х или Х, принадлежащего любому целому типу.    Применение ORD(X) к логическому, символьному и перечисляемому типам дает положительное целое число в диапазоне от 0 до 1 (логический тип), от 0до 255 (символьный), от 0до 65535 (перечисляемый). Тип-диапазон сохраняет все свойства базового порядкового типа, поэтому результат применения к нему функции ORD(X) зависит от свойств этого типа.
    К порядковым типам можно также применять функций:
PRED(X) - возвращает предыдущее значение порядкового типа (значение, которое соответствует порядковому номеру ORD(X)-1), т.е. ORD(PRED(X)) - ORD(X) - I;
SUCC(X) - возвращает следующее значение порядкового типа, которое соответствует порядковому номеру ORD(X)+1, т.е. ORD(SUCC(X)) - ORD(X) +1.
Например, если в программе определена переменная
var с : char;
"с":= ‘5’:,
то функция PRED(C) вернет значение '4', а функция SUCC(C) - значение '6'.
   Если представить себе любой порядковый тип как упорядоченное множество значений, возрастающих слева направо и занимающих на числовой оси некоторый отрезок, то функция PRED(X) не определена для левого, a SUCC(X) - для правого конца этого отрезка.
Целые типы. Диапазон возможных значений целых типов зависит от их внутреннего представления, которое может занимать один, два или четыре байта. В табл. 4 приводится название целых типов, длина их внутреннего представления в байтах и диапазон возможных значений.
   Таблица 4 

                      Целые типы

Длина, байт Название типа  Диапазон значений 
            1   byte  0....255
            1   shortint   -128...127
            2    word   0..65535
            2    integer         -32768...32767
 4   longint   -21747483648..2147483647
                                                 
     При использовании процедур и функций с целочисленными параметрами следует руководствоваться «вложенностью» типов, т.е. везде, где может использоваться WORD, допускается использование BYTE (но не наоборот) , в LONGINT «входит» INTEGER, который, в свою очередь, включает в себя SHORTWT.
      Перечень процедур и функций, применимых к целочисленным типам, приведен в табл.5. Буквами />,$,и>,1,/обозначены выражения соответственно типа BYTE, SHORTINT, WORD, INTEGER и LONGINT, x выражение любого из этих типов; буквы vb,vs,vw,vi,vl,vx обозначают переменные соответствующих типов. В квадратных скобках указывается, необязательный параметр.
Таблица 5
                Встроенные процедуры и функции, применимые к целым типам
   
           Обращение     Тип результата        Действие
         abs(x)                          x   Возвращает модуль X
          chr(b)            Char  Возвращает символ по его коду
    dec(vx[,i])          Процедура Уменьшает значение vx на  i, при отсутствии i- на 1.
 Inc(vx[,i])            Процедура Увеличивает  значение vx на  i, при отсутствии i- на 1.
      hi(i)          Byte  Возвращает старший байт аргумента
       hi(w)         То же      То же
      lo(i)            '' Возвращает младший байт аргумента
       lo(w)            ''          То же
  odd(i)      boolean  Возвращает True, если аргумент - не чётное число, false если чётное
 random(w)      Как у параметра  Возвращает псевдослучайное число, равномерно распределённое на интервале 0<=x<w
sqr (x)    То же  Возвращает квадрат аргумента
   swap(i)       integer  Меняет местами байты в слове
  swap(w)         word       То же
 
   При действиях с целыми числами тип результата будет соответствовать типу операндов, а если операнды относятся к различным целым
типам, типу того операнда, .который имеет максимальную мощность
(максимальный диапазон значений). Возможное переполнение результа
та никак не контролируется, что может привести к недоразумениям, на
пример: 

Var
   а : integer: х, у : real;
BEGIN
   а := 32767; {Максимально возможное значение типа INTEGER}
   х := а+2;
   у := longint(а)+2;
   wrlteln(x:10:0, y:10:0);
END.
В результате прогона программы получим
-32767 32769
   Логический тип. Значениями логического типа может быть одна и: предварительно объявленных констант FALSE (ложь) или TRUE (истина). Для них справедливы правила:
ordjfalse) = 0;
ord(true) =1; 
false < true;
succ(false)= true:
pred(true) =- false
   Поскольку логический тип относится к порядковым типам, его можно использовать, например, в операторе
var
  I : Boolean;
  for I := false to true do ....

   Символьный тип. Значением символьного типа является множеств всех символов ПК. Каждому символу приписывается целое число в диапазоне 0...255. Это число служит кодом внутреннего представления сим вола, его возвращает функция ORD.
   Для кодировки используется код ASCII (American Standard Code fc Information Interchange - американский стандартный код для обмена информацией). Это 7-битный код, т.е. с его помощью можно закодирована лишь 128 символов в диапазоне от 0 до 127. В то же время в 8-битном байт! отведенном для хранения символа в Турбо Паскале, можно закодирована в два раза больше символов в диапазоне от 0 до 255. Первая половик символов ПК с кодами 0...127 соответствует стандарту ASCII (табл. 6 Вторая половина символов с кодами 128...255 не ограничена жестким рамками стандарта и .может меняться на ПК разных типов (в прил. приведены некоторые распространенные варианты кодировки этих символов).
Таблица 6
                                     Кодировка символов в соответствии с кодом ASCII
 
     Код  Символ Код Символ Код Символ Код Символ
   0 NULL 32 BL 64    @ 96    '
   1 SOH 33        !   65      A    97     a
   2 STX 34        ''  66      B  98     b
  3 ETX  35     # 67      C   99      c
   4 EOT    36       $   68   D    100     d
   5 ENQ  37      %   69      E   101      e
   6 ACK 38      &   70     F    102      f
    7 BEL  39       '  71    G   103       g
     8 BS   40     (   72     H  104      h
    9 HT  41      )  73      I   105      i
   10 LF   42     *   74     J   106     j
    11 VT   43      +    75       K    107     k
    12 FF   44       ,    76      L    108       l
   13      CR   45      -     77       M   109      m
   14     SO   46      .      78       N   110     n
   15     SI  47      /    79       O  111      o
   16    DEL   48      0     80       P   112     p
   17    DC1   49      1    81       Q    113      q
   18     DC2   50       2    82     R    114      r
   19     DC3   51       3    83      S  115      s
   20     DC4   52       4    84       T   116       t
   21     NAK    53       5    85       U    117       u
   22      SYN    54       6    86       V    118     v
   23      ETB   55       7    87      W   119      w
   24     CAN    56      8    88       X    120       x
   25      EM    57      9     89        Y   121        y
   26     SUB    58      :     90       Z   122        z
   27     ESC    59      ;     91        [    123       {
   28      FS    60      <     92       \    124       |
   29     GS    61       =     93       ]    125      }
   30    RS     62       >      94      ^    126      ~
   31     US    63       ?      95       _    127      ^


   Символы с кодами 0...31 относятся к служебным кодам. Если эти коды используются в символьном тексте программы, они считаются пробелами. При использовании их в операциях ввода-вывода они могут иметь следующее самостоятельное значение:
Символ Код Значение
BEL 7 Звонок;
вывод на экран этого символа сопровождается звуковым сигналом
НТ 9 Горизонтальная табуляция;
при выводе на экран смещает курсор в позицию, кратную 8, плюс 1 (9,17, 25 и т.д.)
LF 10 Перевод строки;
при выводе его на экран все последующие символы будут выводиться, начиная с той же позиции, но на следующей строке
VT 11 Вертикальная табуляция;
при выводе на экран заменяется специальным знаком
FF 12 Прогон страницы;
при выводе на принтер формирует страницу, при выводе на экран заменяется специальным знаком
CR 13 Возврат каретки;
вводится нажатием на клавишу Enter (при вводе с помощью READ или READLN означает команду «Ввод» и в буфер ввода не помещается; при выводе означает команду «Продолжить вывод с начала текущей строки»)
SUB 26 Конец файла;
вводится с клавиатуры нажатием Ctrl-Z; при выводе заменяется специальным знаком
ESC 27 Конец работы;
вводится с клавиатуры нажатием на клавишу ESC; при
выводе заменяется специальным знаком
К типу CHAR применимы операции отношения, а также встроенные функции:
CHR(B) - функция типа CHAR; преобразует выражение В типа БУГЕ в символ и возвращает его своим значением;
UPCASE(CH) - функция типа CHAR; возвращает символ в верхнем регистре, если он определен для аргумента СН типа CHAR; в противном случае возвращает сам символ СН, например:
var
  с1,с2 : char; 
begin
  c1:=UpCase('s’);
  c2:=UpCase('ф'};
  writeln(d, ' ' ,c2);
end. 
В результате прогона этой программы на экран будет выдано
S ф
так как функция UPCASE не обрабатывает кириллицу.
   Перечисляемый тип. "Перечисляемый тип задается перечислением тех значений, которые он может получать. Каждое значение именуется некоторым идентификатором и располагается в списке, обрамленном круглыми скобками, например:
type
  colors = (red, white, blue);
   Применение перечисляемых типов делает программы нагляднее. Если, например, в программе используются данные, связанные с месяцами года, то такой фрагмент программы:
type
  ТипМесяц=(янв,фев,мар,апр,май,июн,июл,aвг,сен,окт,ноя,дек); 
var
  месяц : ТипМесяц;
  if месяц-авг then writetn('Хорошо бы поехать к морю!'); 
был бы, согласитесь, очень наглядным. Увы! В Турбо Паскале нельзя использовать кириллицу в идентификаторах, поэтому мы вынуждены писать так:
type .
  TypeMonth=(Jan.feb.mar.may,Jun,jul,aug,sep,oct,nov,dec);
var
  month: TypeMonth;
  if'month=aug then writeIn('Хорошо бы поехать к морю!'): 
   Соответствие между значениями перечисляемого типа и порядковыми номерами этих значений устанавливается порядком перечисления: первое значение в списке получает порядковый номер 0, второе - 1 и т.д. Максимальная мощность перечисляемого типа составляет 65535 значений, поэтому фактически перечисляемый тип задает некоторое подмножество целого типа WORD и может рассматриваться как компактное объявление сразу группы целочисленных констант со значениями 0, 1 и т.д.
   Использование перечисляемых типов повышает надежность программ благодаря возможности контроля тех значений, которые получают соответствующие переменные. Пусть, например, заданы такие перечисляемые типы:
type
  colors = (black, red, white); 
  ordenal= one, two, three); 
  days = (tnonday, tuesday, Wednesday);
С точки зрения мощности и внутреннего представления все три типа эквивалентны:
ord(black)-0, .... ord(white)-2,
ord(one)-0, ... , ord(three)-2,
ord(monday)-0, ... , ord(wednesday)-2.
Однако, если определены переменные
var
col : colors; 
num : ordenal; 
day : days; то допустимы операторы
col := black; 
num := succ(two);
day := pred(tuesday); но недопустимы
col := one; 
day := black: и т.д:
   Как уже упоминалось, между значениями перечисляемого типа и множеством целых чисел существует однозначное соответствие, задаваемое функцией ORD(X). В Турбо Паскале допускается и обратное преобразование: любое выражение типа WORD можно преобразовать в значение перечисляемого типа, если только значение целочисленного выражения не превышает мощности перечисляемого типа. Такое преобразование достигается применением автоматически объявляемой функции с именем перечисляемого типа (см. п. 4.4). Например, для рассмотренного выше объявления типов эквивалентны следующие присваивания:
col := one; 
col := colors(O); Разумеется, присваивание
col := 0; будет недопустимым.
   Переменные любого перечисляемого типа можно объявлять без предварительного описания этого типа, например:
var
col : (black, white, green);
   Тип-диапазон. Тип-диапазон есть подмножество своего базового типа, в качестве которого может выступать любой порядковый тип, кроме типа-диапазона.
Тип-диапазон задается границами своих значений внутри базового типа:
< мин. знач. >.. <макс.знач. >
   Здесь <мин.знач.> - минимальное значение типа-диапазона; <макс.знач-> - максимальное его значение. Например:
type
digit - '0'..'9'; dig2 - 18 .. 57:
   Тип-диапазон необязательно описывать в разделе TYPE, а можно указывать непосредственно при объявлении переменной, например:
var
date 1..31: month 1..12: Ichr 'A'..'Z':.
   При определении типа-диапазона нужно руководствоваться следующими правилами:
• два символа «..» рассматриваются как один символ, поэтому между ними недопустимы пробелы;
• левая граница диапазона не должна превышать его правую граничу.
   Тип-диапазон наследует все свойства своего базового типа, но с ограничениями, связанными с его меньшей мощностью. В частности, если определена переменная

Type
  days = (mo.tu.we,th.fr.sa.su); 
  WeekEnd = sa .. su; 
var 
  w : WeekEnd:
  w := sa; 
то ORD{W} вернет значение 5 , в то время как PRED(W) приведет к ошибке.

 
                Вверх

4.3.2. Вещественные типы


   В отличие от порядковых типов, значения которых всегда сопоставляются с рядом целых чисел и, следовательно, представляются в ПК абсолютно точно, значения вещественных типов определяют произвольное число лишь с некоторой конечной точностью, зависящей от внутреннего формата вещественного числа.
Таблица 7.

                                    Вещественные типы

Длина,

 байты

Название типа  Мантисса, значащие цифры Диапазон десятичного порядка
      4      single       7...8     -45...+38
     6      real     11..12      -39...+38
     8      double      15...16     -324...+308
    10     extended      19..20      -4951..+4932
      8       comp         19..20    -2(63)+1...+2(63)-1


 
   Как видно из табл.7, вещественное число в Турбо Паскале занимает от 4 по 10 смежных байт и имеет следующую структуру в памяти ПК: 
                                                   
             s         e        m


     
Здесь s - знаковый разряд числа; е - экспоненциальная часть; содержит двоичный порядок; т - мантисса числа.
Мантисса т имеет длину от 23 (для SINGLE) до 63 (для EXTENDED) двоичных разрядов, что и обеспечивает точность 7...8 для SINGLE и 19...20 для EXTENDED десятичных цифр. Десятичная точка (запятая) подразумевается перед левым (старшим) разрядом мантиссы, но при действиях с числом ее положение сдвигается влево или вправо в соответствии с двоичным порядком числа, хранящимся в экспоненциальной части, поэтому действия над вещественными числами называют арифметикой с плавающей точкой (запятой). 
   Как видим, Турбо Паскаль характеризуется богатой гаммой веществ венных типов, однако доступ к типам SINGLE, DOUBLE и EXTENDED возможен только при особых режимах компиляции. Дело в том, что эти типы рассчитаны на аппаратную поддержку арифметики с плавающей точкой и для их эффективного использования в состав ПК должен входить арифметический сопроцессор 8087, 80287 или их отечественный аналог К181ОВМ87.      Компилятор Турбо Паскаля позволяет создавать программы, работающие на любых ПК (с сопроцессором или без него) и использующие любые вещественные типы. Необходимая для этого настройка компилятора описана в прил. 1. В процессе запуска Турбо Паскаль проверяет состав аппаратных средств и выявляет наличие или отсутствие сопроцессора.
В некоторых случаях бывает необходимо отключить автоконтроль. Для этого перед запуском Турбо Паскаля следует дать такую команду ДОС:
set 87-N Команда
set 87-Y напротив, включает автоконтроль - эта команда активна по умолчанию.
   Отметим, что арифметический сопроцессор всегда обрабатывает числа в формате EXTENDED, а три других вещественных типа в этом случае получаются простым усечением результатов до нужных размеров и применяются в основном для экономии памяти.
   Например, если «машинное эпсилон» (см. пример б в гл.2) вычисляется с помощью такой программы:
{SN+.E+} 
type
  RealType = real; 

var
  epsilon : RealType; 
BEGIN
  epsilon := 1;
  while 1+epsilon/2>1 do 
  epsilon := epsilon/2;
  writeln(epsilon);
END.
то независимо от объявления типа REALTYPE (он может быть SINGLE, REAL, DOUBLE или EXTENDED) на печать будет выдан результат
1.08420217248550Е-0019
что соответствует типу EXTENDED. Чтобы получить правильный результат (например, для типа REALTYPE - REAL он будет 9.09494701772928Е-0013), программу необходимо изменить следующим образом:
<$М+,Е+}
type
  RealType = real; 
var
  epsiIon, epsl : RealType;
BEGIN
  epsilon := 1;
  repeat
  epsilon := epsiIon/2;
  eps1 :=1+epsilon;
until eps1>1; 
wrlteln(2*epsilon) 
END.
   Следует учесть, что тип REAL оптимизирован для работы без сопроцессора. Вели Ваш ПК оснащен сопроцессором, использование типа REAL приведет к дополнительным затратам времени на преобразование REAL к EXTENDED. Поэтому никогда не используйте REAL на ПК с сопроцессором, т.к. дополнительные затраты времени на преобразование
типов могут свести на нет все преимущества сопроцессора. При разработке программ, критичных ко времени счета, следует заменять его типами SINGLE или DOUBLE: по сравнению с типом REAL скорость вычислении на машинах с сопроцессором в этом случае увеличивается в 2...3 paза.
   Если в ПК нет арифметического сопроцессора, скорость обработки данных всех вещественных типов приблизительно одинакова. 
   Особое положение в Турбо Паскале занимает тип СОМР, который трактуется как вещественное число без экспоненциальной и дробной частей. Фактически, СОМР - это «большое» целое число со знаком, сохраняющее 19...20 значащих десятичных цифр (во внутреннем представлении СОМР занимает 8 смежных байт). В то же время в выражениях,
СОМР полностью совместим с любыми другими вещественными типами:
над ним определены все вещественные операции, он может использоваться как аргумент математических функций и т.д. Наиболее подходящей областью применения типа СОМР являются бухгалтерские расчеты: денежные суммы выражаются в копейках и действия над ними сводятся к операциям с достаточно длинными целыми числами. 
   Для работы с вещественными данными могут использоваться встроенные математические функции, представленные в табл. 8. В этой таблице REAL означает любой вещественный тип, INTEGER - любой целый тип.
табл 8
                                  Встроенные математические функции Турбо- Паскаля
                 
Обращение   Тип параметра  Тип результата    Примечание
abs(x)  real, integer  Тип аргумента  Модуль аргумента
ArcTan(x)  real   real     Арктангенс(радианы)
  cos(x)   То же   То же       Косинус, угол в радианах
 exp(x)     ''       ''            Экспонента
   frac(x)      ''         ''     Дробная часть числа
     int(x)        ''          ''         Целая часть числа
        in(x)       ''           ''        Логарифм натуральный
       pi        -         real         П=3.141592653
    Random         -        real   Равномерное псевдослучайное число 0<=x<1
  Random(x)      integer      integer   Равномерное псевдослучайное целое число 0<=i<x 
  Randomize         -          -    Инициация датчика псевдослучайных чисел
 sin(x)   real       real   Синус, угол в радианах
 sqr(x)   real, integer   тип аргумента  Квадрат аргумента
  sqrt(x)   real  real  Корень квадратный

                                  Вверх


                          4.4. СТРУКТУРИРОВАННЫЕ ТИПЫ

  Любой из структурированных типов (а в Турбо Паскале их четыре: массивы, записи, множества и файлы) характеризуется множественностью образующих этот тип элементов, т.е. переменная или константа структурированного типа всегда имеет несколько компонентов. Каждый компонент, в свою очередь, может принадлежать структурированному типу, что позволяет говорить о возможной вложенности типов. В Турбо
Паскале допускается произвольная глубина вложенности типов, однако суммарная длина любого из них во внутреннем представлении не должна превышать 65520 байт.
В целях совместимости со стандартным Паскалем в Турбо Паскале
разрешается перед описанием структурированного типа ставить зарезерви
рованное слово PACKED, предписывающее компилятору, по возможности, экономить память, отводимую под объекты структурированного типа; но компилятор фактически игнорирует это указание: «упаковка»
данных в Турбо Паскале осуществляется автоматически везде, где это возможно.
 
                               Вверх     

                                   4.4.1. Массивы

    Массивы в Турбо Паскале во многом схожи с аналогичными типами данных в других языках программирования. Отличительная особенность массивов заключается в том, что все их компоненты суть данные одного типа (возможно, структурированного). Эти компоненты можно легко упо-рядочить и обеспечить доступ к любому из них простым указанием его порядкового номера, например.
type digit =- array [0..9] of char;
atrix = array [byte] of single;
var
m : matrix; d : digit; i : integer;
...
m[17]:= ord(d[i-1])/10; :
Описание типа массива задается следующим образом:
<имя пшпа>=ARRAY[ <сп.инд.типов>] OF<тип>;
Здесь <имя типа> - правильный идентификатор;
ARRAY, Of - зарезервированные слова (массив, из);
<сп.инд.тшюв> - список из одного или нескольких индексных
пов, разделенных запятыми; квадратные скобки, обрамляющие список, - требование синтаксиса:
<тип> - любой тип Турбо Паскаля.
В качестве индексных типов в Турбо Паскале можно использовать
любые порядковые типы, кроме LONGINT и типов-диапазонов с базовым
типом LONGINT.
Определить переменную как массив можно и непосредственно при
описании этой переменной, без предварительного описания типа массива,
например:
var
а,Ь : array [1..10] of real;
Обычно в качестве индексного типа используется тип-диапазон, в
котором задаются границы изменения индексов. Так как тип <тип>, идущий за словом Of, - любой тип Турбо Паскаля, то он может быть в
частности, и другим массивом, например:
type
mat =- array [0..5] of array [-2..2] of array [char] of
byte;
Такую запись можно заменить более компактной:
type
mat = array [0. .5,-2..2,char] of byte;
Глубина вложенности структурированных типов вообще, а следова
тельно, и массивов - произвольная, поэтому количество элементов в спи
ске индексных типов (размерность массива) не ограничено, однако сум
марная длина внутреннего представления любого массива, как уже гово
рилось, не может быть больше 65520 байт. В памяти ПК элементы массива
следуют друг за другом так, что при переходе от младших адресов к
старшим наиболее быстро меняется самый правый индекс массива. Если,
например,
var
a : array [1..2,1. .2] of byte;
a[1,1]:=1; a[2,1]:=2;
a[1,2]:=3; a[2,2]:=4;
.......
то в памяти последовательно друг за другом будут расположены байты со значениями 1, 3, 2, 4 . Это обстоятельство может оказаться важным при использовании встроенной процедуры копирования памяти MOVE.

В Турбо Паскале можно одним оператором присваивания передать все элементы одного массива другому массиву того же типа, например:
var a,b : array [ 1. .5] of single;
...
a:= b;
После этого присваивания все пять элементов массива А получат те же значения, что и в массиве В. Однако над массивами не определены операции отношения. Нельзя, например, записать
if а = b then ... Сравнить два массива можно поэлементно, например:
var
a,b : array [ 1. .5] of single; eq : Boolean; i : byte;
eq:= true;
for i:=1 to 5 do
if a[i] <> b[i] then eq:=false; 
if eq then ...

                       Вверх


 

4.4.2 Сортировка массивов



   При решении задачи сортировки обычно выдвигается требование минимального использования дополнительной памяти, из которой вытекает недопустимость применения дополнительных массивов.
Для оценки быстродействия алгоритмов различных методов сортировки, как правило, используют два показателя:

Количество присваивания;
Количество сравнений;

Все методы сортировки можно разделить на две большие группы:

Прямые методы сортировки;
Улучшенные методы сортировки;

Прямые методы сортировки по принципу, лежащему в основе метода, в свою очередь разделяются на три группы:

1) сортировка вставкой (включением)
2) сортировка выбором (выделением)
3) сортировка обменом (‘пузырьковая’ сортировка )

Улучшенные методы сортировки основываются на тех же принципах, что и прямые, но используют некоторые оригинальные идеи для ускорения процесса сортировки. Прямые методы на практике используются довольно редко, так как имеют относительно низкое быстродействие. Однако они хорошо показывают суть основанных на них улучшенных методов. Кроме того, в некоторых случаях (как правило, при небольшой длине массива или особом расположении элементов массива) некоторые из прямых методов могут даже превзойти улучшенные методы.


Сортировка вставкой


Принцип метода:
Массив разделяется на две части: отсортированную и не отсортированную. Элементы из не отсортированной части поочерёдно выбираются и вставляются в отсортированную часть так, чтобы не нарушить в ней упорядоченность элементов. В начале работы алгоритма в качестве отсортированной части массива принимают только один первый элемент, а в качестве не отсортированной части – все остальные элементы.
Таким образом, алгоритм будет состоять из n-1-го прохода (n- размерность массива), каждый из которых будет включать четыре действия:
Взятие очередного i- го не отсортированного элемента и сохранение его в дополнительной переменной;
Поиск позиции j в отсортированной части массива, в которой присутствие взятого элемента не нарушит упорядоченности элементов; 
Сдвиг элементов массива от i- 1- го до j- 1- го вправо, чтобы освободить найденную позицию вставки;
Вставки взятого элемента в найденную j- ю позицию.

Для реализации данного метода можно предложить несколько алгоритмов, которые будут отличатся способом поиска позиции вставки.
Рассмотрим схему реализации одного из возможных алгоритмов.
Схематично описанные действия одного прохода можно представить следующим образом

Схема сортировки методом вставки. Слева в кружке указан номер прохода

Программа, реализующая рассмотренный алгоритм, будет иметь следующий вид:
Демонстрация


Program InsertionSort;
Uses Crt;
Const
N= 20; {длина массива}
Type
Tvector= array [1..n] of real;
Var
Vector : Tvector;
B : Real;
I, j, k : integer;
Begin 
ClrScr;
Writeln (‘Введите элементы массива:’);
For i:=1 to n do Read (Vector[i]); Readln;
{-----------------------------------------------------------------}

for i:= 2 to n do 
begin 
B:= Vector[i]; {Взятие не отсортированного элемента}
{Цикл поиска позиции всиавки}
j:= 1;
while (B> Vector[j]) do 
j:= j+1; {После окончания цикла индекс j фиксирует позиции вставки}
{Цикл сдвига элементов для освобождения позиции вставки}
for k:= i-1 downto j do
Vector[k+1] := Vector [k];
{Вставка взятого элемента на найденную позицию }
Vector[j] := B;
End;
{--------------------------------------------------------------------------}

writeln(‘Отсортированный массив:’);
for i := 1 to n do write (Vector[i]:8:2);
writeln;
end.


Сортировка выбором 


Принцип метода:
Находим (выбираем) в массиве элемент с минимальным значением на интервале от 1- го элемента до n- го (последнего) элемента и меняем его местами с первым элементом. На втором шаге находим элемент с минимальным значением на интервале от 2- го до n –го элемента и меняем местами со вторым элементом.
И так далее для всех элементов до n-1- го. 

Рассмотрим схему алгоритма прямого выбора.


Программа, реализующая метод выбора, будет иметь следующий вид:
Демонстрация


Program SelectionSort;
Uses Crt;
Const
N= 20 ; {длина массива}
Type 
Tvector = array [1..n] of real;
Var
Vector : Tvector;
Min : Real;
Imin, S : Integer;
I: Integer;
Begin
ClrScr;
Writeln (‘Введите элементы массива:’);
For i:= 1 to n do Read (Vector[i]); Readln;
{----------------------------------------------------------------------}
for S:=1 to n-1 do 
begin
{Поиск минимального элемента в диапазоне}
{от S- го элемента до n- го}
min := vector[S];
Imin := S;
For i := S+1 to n do
If vector[i] < min then
Begin
Min:= vector[i];
Imin := i;
End;
{Обмен местами минимального и S –го элементов}
vector[imin] := vector[S];
vector[S] := Min;
end;
{--------------------------------------------------------------------------}
writeln(‘Отсортированный массив:’);
for i:= 1 to n do write (vector[i] :8:2);
writeln;
end.


Сортировка обменом (‘пузырьковая’ сортировка)


Принцип метода:
Слева направо поочерёдно сравниваются два соседних элемента, и если их взаиморасположение не соответствует заданному условию упорядоченности, то они меняются местами. Далее берутся два следующих соседних элемента и так далее до конца массива.
После одного такого прохода на последней n- ой позиции массива будет стоять максимальный элемент(‘всплыл’ первый ‘пузырёк’). Поскольку максимальный элемент уже стоит на своей последней позиции, то второй проход обменов выполняется до n-1- го элемента. И так далее. Всего требуется n-1 проход.
Рассмотрим схему алгоритма сортировки методом прямого обмена по не убыванию.

Программа, реализующая метод обмена(‘пузырька’ ), будет иметь следующий вид:

Program BubleSort;
Uses Crt;
Const
N= 20; {Длина массива}
Type
Tvector = array [1..n] of real;
Var
Vector : Tvector ;
B : Real;
I, k: integer;
Begin
ClrScr;
Writeln {‘Введите элемент массива:’};
For i:= 1 to n do Read (Vector[i]); Readln;
{-------------------------------------------------------------}
for k:= n downto 2 do 
{‘Всплывание ’ очередного максимального элемента на k- ю позицию } 
for i:= 1 to k-1 do 
if Vector[i] > Vector[i+1] then 
begin
B:= Vector[i];
Vector[i+1] :=B
End;
{------------------------------------------------------------------}
writeln {‘Отсортированный массив:’};
for i:= 1 to n do write (Vector[i]:8 :2);
writeln;
end.


Сравнение прямых методов сортировки



Теоретическиеи практические исследования алгоритмов прямых методов сортировки показали, что как по числу присваивания они имеют квадратичную зависимость от длины массива n. Исключение составляет метод выбора, который имеет число присваиваний порядка n*ln(n). Это свойство алгоритма выбора полезно использовать в задачах сортировки в сложных структурах данных, когда на одно сравнение выполняется большое число присваиваний. В таких задачах метод выбора успешно конкурирует с самыми быстрыми улучшенными методами сортировки.
Принцип двоичного поиска:
Исходный массив делится пополам и для сравнения выбирается средний элемент. Если он совпадает с искомым, то поиск заканчивается . Если же средний элемент меньше искомого, т овсе элементы левее его также будет меньше искомого. Следовательно, их можно исключить из зоны дальнейшего поиска, оставив только правую часть массива. Аналогично, если средний элемент больше искомого, то отбрасывается правая часть, а остаётся левая.
На втором этапе выполняется аналогичные действия над оставшейся половине массива.
И так далее, пока элемент не будет найден, или длина зоны поиска станет равной нулю. В последнем случае искомый элемент найден не будет.
Рассмотрим схему алгоритма двоичного поиска.

Одно из возможных вариантов программы, реализующей метод двоичного поиска, имеет следующий вид:

Program BinSearch;
Uses Crt;
Const
N= 20; {Длина массива} 
Type
Tvector = array [1..n] of real;
Var
Vector : Tvector; {Исходный массив}
X : Real; { Искомый элемент}
L, R: Integer; {Текущие границы зоны поиска}
I : Integer;
Begin
ClrScr;
Writeln {‘Введите элементы массива:’};
For i:= 1 to n do Read (Vector[i]); Readln;
Write(‘Введите искомый элемент:’);
Readln(x);
{---------------------------------------------------------------------}
L:= 1; R:= n;
While(L<=R) do (Покане пересекутся границы )
Begin
I:=(L+R) div 2;{Индекс среднего элемента}
If Vector[i] = X then
Break{Выход из цикла поиска т.к элемент найден}
Else
If Vector[i]< X thenL :=i+1
Else R:=i- 1;
End;{while}
If Vector[i]= X then 
Writeln (‘Искомый элемент найден на позиции’, i:3)
Else
Writeln (‘Искомый элемент не найден’);
{-----------------------------------------------------------------------} 
end.

           Вверх


 
                                    4.4.3. Записи

 Запись - это структура данных, состоящая из фиксированного числа компонентов, называемых полями записи. В отличие от массива, компоненты (поля) записи могут быть различного типа. Чтобы можно было ссылаться на тот или иной компонент записи, поля именуются.
Структура объявления типа записи такова:
<имя типа» - RECORD <сп.полей> END
Здесь <имя типа> - правильный идентификатор;
RECORD, END - зарезервированные слова (запись,конец);
<сп.полей> - список полей; представляет собой последовательность разделов записи, между которыми ставится точка с запятой.
Каждый раздел записи состоит из одного или нескольких идентификаторов полей, отделяемых друг от друга запятыми. За идентификатором (идентификаторами) ставится двоеточие и описание типа поля (полей), например:
type
Birthday - record day,month : byte; year : word end; var
a,b : Birthday;
В этом примере тип BIRTHDAY (день рождения) содержит три поля с именами DAY, MONTH и YEAR (день, месяц и год); переменные А и В содержат записи типа BirthDay.
Как и в массиве, значения переменных типа записи можно присваивать другим переменным того же типа, например
а := b; К каждому из компонентов записи можно получить доступ, если

использовать составное имя, т.е. указать имя переменной, затем точное имя поля:
a.day := 27;
b.year := 1939; Для вложенных полей приходится продолжать уточнения:
var
с : record
name : string;
bd : Birthday
end;
if c.bd.year - 1939 then ...
Чтобы упростить доступ к полям записи, используется опеатор
присоединения WITH:
WITH <переменная> DO <оператор>
Здесь WITH, DO - ключевые слова (с, делать);
<переменная> - имя переменной типа запись, за которым, возможно следует список вложенных полей;
<оператор> - любой оператор Турбо Паскаля.
Например:
with c.bd do month := 9;
Это эквивалентно
with с do with bd do month := 9;
или
with c, bd do month := 9;
или
c.bd.month := 9;
Турбо Паскаль разрешает использовать записи с так называем
вариантными полями, например:
type
forma = record
name : string;
case byte of
0 : (BirthPlace : string[40] );
1 : (country : string[20];
EntryPort : string[20];
EntryDate : 1..31;

ExitDate : 1..31)

end;

В этом примере тип FORMA определяет запись с одним фиксированным полем NAME и вариантной частью, которая задается предложением CASE...OF. Вариантная часть состоит из нескольких вариантов (в примере - из двух вариантов: 0 и 1). Каждый вариант определяется конец
выбора, за которой следует двоеточие и список полей, заключенный в круглые скобки. В любой записи может быть только одна вариантная часть, и, если она есть, она должна располагаться за всеми фиксированными полями.
Замечательной особенностью вариантной части является то обстоятельство, что все заданные в ней варианты как бы "накладываются" друг на друга, т.е. каждому из них выделяется одна и та же область памяти. Это открывает дополнительные возможности преобразования типов, например:

 var
mem4:record
case byte of
0 : (by array [0..3] of byte);
1 : (wo array (0..1] of word);
2: (lo : longint); 

end;

var
mem4:record
case byte of
0 : (by array [0..3] of byte);
1 : (wo array (0..1] of word);
2: (lo : longint); end;

В этом примере запись МЕМ4 имеет три варианта, каждый из которых занимает в памяти один и тот же участок из 4 байт. В зависимости от того, к какому полю записи мы обращаемся в программе, этот участок может рассматриваться как массив из 4 байт (поле ВУ), массив из двух целых типа WORD (поле WO) или, наконец, как одно целое число типа LONGINT (поле LO). Например, этой записи можно сначала присвоить значение как длинному целому, а затем проанализировать результат по байтам или словам:
var
x : word;
xb: byte;
хi: longInt;
with m do begin
lo := trunc(2*pi*x); If wo[1]=0 then
If by[1]=0 then xb := x[0] else x := wo[0] else xi := lo
end;

Предложение CASE...OF, открывающее вариантную часть, внешне похоже на соответствующий оператор выбора, но на самом деле лишь играет роль своеобразного служебного слова, обозначающего начало вариантной части. Именно поэтому в конце вариантной части не следует ставить END как пару к СA SE... OF. (Поскольку вариантная часть - всегда последняя в записи, за ней все же стоит END, но лишь как пара к RECORD). Ключ выбора в предложении CASE... OF фактически игнорируется компилятором: единственное требование, предъявляемое к нему Турбо Паскалем, состоит в том, чтобы ключ определял некоторый стандартный или предварительно объявленный порядковый тип. Причем сам этот тип никак не влияет ни на количество следующих ниже вариантных полей, ни даже на характер констант выбора. В стандартном Паскале в качестве ключа выбора необходимо указывать некоторую переменную порядкового типа, причем в исполняемой части программы можно присваивать значение этой переменной и таким образом влиять на выбор лей. В Турбо Паскале также можно в поле ключа выбора указывать переменную порядкового типа и даже присваивать ей в программе значение, что однако не влияет на выбор поля: значения констант выбора в Турбо  Паскале могут быть произвольными, в том числе повторяющимися,

например:
type

rec=record

 a: byte;

      b: word;

end;

rec2 = record 

с : longint; 

case x : byte of
1 : (d : word);
2 : (e : record
                    case Boolean of 

3 :(f: reс1);

 3 :(g: single);

 '3':( с: word) 

end) 

end; 

var
r : rec2;

BEGIN
r.x :=255:
if r.e..g=0 then wrIteln('O.K.')

              else writeln(r.e.g)

 END.


В этом примере предложение
                                           case Boolean of
в записи, определяемой в поле Е, объявляет ключом выбора логический тип, который, как известно, имеет лишь два значения - TRUE и FALSL Константы же выбора следующих далее вариантов не только содержат совершенно не свойственные этому типу значения, но и две из них повторяются, а общее количество вариантов - три, а не два, как следовало бы ожидать.
Имена полей должны быть уникальными в пределах той записи, где они объявлены, однако, если записи содержат поля-записи, т.е. вложи. одна в другую, имена могут повторяться на разных уровнях вложенной (см. поле С в последнем примере).

 

                   Вверх         

 

 

4.4.4. Множества 

 

   Множества - это наборы однотипных логически связанных друг с другом объектов. Характер связей между объектами лишь подразумевается программистом и никак не контролируется Турбо Паскалем. Количество элементов, входящих в множество, может меняться в пределах от 0 до 256 (множество, не содержащее элементов, называется пустыми. Именно непостоянством количества своих элементов множества отличаются от массивов и записей. 
   Два множества считаются эквивалентными тогда и только то когда все их элементы одинаковы, причем порядок следования элемента в множестве безразличен. Если все элементы одного множества входят также и в другое, говорят о включении первого множества во второе пустое множество включается в любое другое.
Пример определения и задания множеств:
Type
  DigitChar= set of ‘0’..’9’;
  Digit = set of 0..9;
Var
  S1,s2,s3: digitChar;
  S4,S5,S6:digit;
  S1:=[‘1’,’2’,’3’];
  S2:=[‘3’,’2’,’1’];
  S3:=[‘2’,’3’];
  S4:=[0..3,6];
  S5:=[4,5];
  S6:=[3..9];

   В этом примере множества SI и S2 эквивалентны, а множество S3 включено в 52 , но не эквивалентно ему. Описание типа множества имеет вид:
<имя типа> - SET OF <баз.тип>
Здесь <имя типа> - правильный идентификатор;
SET, OF - зарезервированные слова (множество, из);
<баз..тип> - базовый тип элементов множества, в качестве которого может использоваться любой порядковый тип, кроме WORD, INTEGER, WNGINT.
   Для задания множества используется так называемый конструктор множества: список спецификаций элементов множества, отделяемых друг от друга запятыми; список обрамляется квадратными скобками (см. предыдущий пример). Спецификациями элементов могут быть константы или выражения базового типа, а также - тип-диапазон того же базового типа.
   Над множествами определены следующие операции:
- - пересечение множеств; результат содержит элементы, общие для обоих множеств; например, S4*S6 содержит [3], S4*S5 - пустое множество (см. выше);
+ - объединение множеств; результат содержит элементы первого множества, дополненные недостающими элементами из второго множества:
S 4+S5 содержит [0, 1, 2, 3, 4, 5, 6];
S5=S6 содержит [3, 4, 5, 6, 7, 8, 9];
n разность множеств ; результат содержит элементы из первого множества, которые не принадлежат второму:
S6-S5 содержит [3, 6, 7, 8, 9];
S4-S5содержит [0, 1, 2, 3, 6];

= - проверка эквивалентности; возвращает TRUE, если оба множества эквивалентны;
<> - проверка неэквивалентности; возвращает TRUE, если оба множества неэквивалентны;
<= - проверка вхождения; возвращает TRUE, если первое множество включено во второе;
>= - проверка вхождения; возвращает TRUE, если второе множество помещено в первое;
In - проверка принадлежности; в этой бинарной операции первый элемент - выражение, а второй - множество одного и того же типа; возвращает TRUE, если выражение имеет значение, принадлежащее множеству:

3 in S6 возвращает True
2*2 in S1 возвращает False

    В примере 12, иллистрирующем приёмы работы с множествами, реализуется алгоритм выделения из первой сотни натуральных чисел всех простых чисел. Алгоритм с небольшими изменениями заимствован из [3] . В его основе лежит прием, известный под названием «решето Эратосфена». В соответствии с этим алгоритмом вначале формируется множестве) BEGINSET, состоящее из всех целых чисел в диапазоне от 2 до N. ц множество PRIMERSET (оно будет содержать искомые простые числа) помещается 1. Затем циклически повторяются следующие действия: 
. взять из BEGINSET первое входящее в него число NEXT и
поместить его в PRIMERSET;}
• удалить из BEGINSET число NEXT и все другие числа, кратные
ему, т.е. 2*NEXT, 3*NEXT и т.д.
   Цикл повторяется до тех пор, пока множество BEGINSET не станет пустым.
Эту программу нельзя использовать для произвольного N, так как в
любом множестве не может быть больше 256 элементов.

Пример 12.

{Выделение всех простых чисел из первых N целых}

Const
  N=100 ; {Количество элементов исходного множеств}
Type
  SetOfNumber= Set of 1…N;
Var
  N1,next, i: Word; {Вспомогательные переменные}
  BeginSet, {Исходное множество}
  PrimerSet ; SetOfNumber; {Множество простых чисел}
Begin
  BeginSet:=[2..N]; {Создать исходное множество }
  PrimerSet:=[1]; {Первое простое число}
  Next :=2; {Следующее простое число}
  While BeginSet <> [] do {Начало основного цикла}
  Begin
    N1:= next; {N1-число, кратное очередному простому(next)}
    While N1<=N do {Цикл увеличения из исходного множества из непростых чисел:}
    Begin
      BeginSet :=BeginSet-[N1];
      N1:=N1+next; {Следующее кратное}
    End {Конец цикла удаления}
    PrimerSet:= PrimerSet+[Next];
    Repeat {Получить следующее простое, которое есть первое не вычеркнутое из исходного множества}
    Inc(Next);
    Until (Next in BeginSet) or (next<N);
  End; {Конец основного цикла}
{Вывод результата }
  for i:= 1 to N do 
  If i in PrimerSet then write (i:8);
  Writeln;
End.
                   Вверх   


                                                     4.5. СТРОКИ

Тип STRING (строка) в Турбо Паскале широко используется для обработки текстов. Он во многом похож на одномерный массив символов ARRAY [O..N] OF CHAR, однако, в отличие от последнего, количество символов в строке-переменной может меняться от 0 до N, где N - максимальное количество символов в строке. Значение N определяется объявлением типа STRINGfN] и может быть любой константой порядкового типа, но не больше 255. Турбо Паскаль разрешает не указывать N, в этом случае длина строки принимается максимально возможной, а именно А-255.
   Строка в Турбо Паскале трактуется как цепочка символов. К любому символу в строке можно обратиться точно так же, как к элементу одномерного массива ARRAY[O..N] OF CHAR, например:

                         Var

                   St : String ;

                        ……..

                   if st[5]= ‘A’ then………

           Самый первый байт в строке имеет индекс 0 и содержит текущую длину строки. Первый значащий символ строки занимает второй байт и имеет индекс 1. Над длиной строки можно осуществлять необходимые действия и таким способом изменять длину. Например, удалить из строки все ведомые пробелы можно следующим образом:





                 Var

                   St : Str[10];

                I : Word;

                  …………..

                    I:=10;

               While (st[i]=’’) and (i<>0) do

                 Begin

               Dec(i);

            St[10]:=chr(i)

                     End;

         Значение ORD(st[0]), т.е. текущую длину строки, можно получить и с помощью 

функции LENGTH(st), например:

                 while (legth (st) <>0) and (st[legth(st)]=’’) do

                  St [0]:= chr (legth(st)-1)

К строкам можно применить операцию <+>- сцепление, например:

                    St:= ‘a’ + ‘b’;

                    ST:= St + ‘c’; {St содержит ‘abc’}

Если длина сцепленной строки превысит максимально допустимую длину N, то «лишние» символы отбрасываются. Следующая программа, например, напечатает символ 1:

                   Var

              St: string[1];

                  Begin

                 St:= ‘123’; writeln(st)

                             End.

            Все остальные действия над строками и символами реализуются с помощью 

встроенных процедур и функций.

CONCAT(S1 [,S2, ... ,SN])- функция типа STRING; возвращает строку, 

представляющую собой сцепление строк-параметров SI, S2,... , SH.

COPY(ST, INDEX, COUNT) - функция типа STRING; копирует из строки ST COUNT символов, начиная с символа с номером INDEX. 

DELETE(ST, INDEX, COUNT) - процедура; удаляет COUNT сим-

волов из строки ST, начиная с символа с номером INDEX. 

INSEBT(SUBST, ST, INDEX) - процедура; вставляет подстроку

SUBST в строку ST, начиная с символа с номером INDEX.

LENGTH (ST) - функция типа INTEGER; возвращает длину строки
ST.
I
POS (SUBST, ST) - функция типа INTEGER; отыскивает в строке ST первое 
вхождение подстроки SUBST и возвращает номер позиции, с|
которой она начинается; если подстрока не найдена, возвращается ноль. 

STB(X [:WIDTH [:DECIMALS]], ST) - процедура; преобразует

число X любого вещественного или целого типов в строку символов ST так,

как это делает процедура WRITELN перед выводом; параметры WIDTm

и DECIMALS, если они присутствуют, задают формат преобразования

            WIDTH определяет общую ширину поля, выделенного под соответствующее символьное представление вещественного или целого числа X, а DECIMALS - количество символов в дробной части (имеет смысл только в том случае, когда X - вещественное число).
      VAL(ST, X, CODE) - процедура; преобразует строку символов ST во внутреннее представление целой или вещественной переменной X, которое определяется типом этой переменной; параметр CODE содержит ноль, если преобразование прошло успешно, и тогда в X помещается результат преобразования, в противном случае он содержит номер позиции в строке ST, где обнаружен ошибочный символ, и в этом случае содержимое X не меняется; ведущие пробелы в строке ST должны отсутствовать. 
      UPCASE(CH) - функция типа CHAR; возвращает для символьного выражения СН
которое должно представлять собой строчную латинскую букву, соответствующую заглавную букву; если значением СН является любой другой символ, функция возвращает его без преобразований. 
Примеры: 
               var 
               х : real; 
              у : integer: 
                     Jt,st1: string; 
           st:-concat('12','345'); {строка st содержит 12315} 
                st1:-copy(st,3,length)Jt)-2); Ut1 содержит 345} 
                Insert('-',st1,2); (строка st1 содержит 3-45)
         delete(st,pos('2'.it),3): • строка st содержит 15} 
             str(pi :6:2,st); строка st содержит 3.14V]
         st1:-'3,1415'; 
            val(x,st1,y): {у содержит 2, x остался без изменения)

Операции отношения -, о, >, <, >-, <- выполняются над двумя

строками посимвольно, слева направо с учетом внутренней кодировки

символов (см. табл. 7). Если одна строка меньше другой по длине, недостающие символы короткой строки заменяются значением CHR(O) .

Следующие операции отношения дадут значение TRUE: 

'Turbo' < 'Turbo Pascal' 
'Паскаль' > 'Turbo Pascal'
 
           Вверх   

  4.6. СОВМЕСТИМОСТЬ И  ПРЕОБРАЗОВАНИЕ ТИПОВ

    Как уже неоднократно отмечалось, Турбо Паскаль - это типизированный язык. Он построен на основе строгого соблюдения концепции типов, в соответствии с которой все применяемые в языке операции определены только над операндами совместимых типов. При обсуждении операций над вещественными данными мы уже затрагивали проблему совместимости вещественных и целых типов. Аналогичные, проблемы возникают при операциях над строками разной длины, строками и символами и т.д. 
Ниже приводится более полное определение совместимости типов.

    Два типа считаются совместимыми, если:

• оба они есть один и тот же тип;

• оба вещественные;

• оба целые;

• один тип есть тип-диапазон второго типа;

« оба являются типами-диапазонами одного и того же базового типа;

• оба являются множествами, составленными из элементов одного и того же базового типа;

. оба являются упакованными строками (определены с предшествующим словом PACKED) одинаковой максимальной длины;

. один тип есть тип-строка, а другой - тип-строка, упакованная строка или символ;

. один тип есть любой указатель, а другой - нетипизированный указатель;

. один тип есть указатель на объект, а другой - указатель на родственный ему объект;

. оба есть процедурные типы с одинаковыми типом результата (для типа-функции)

, количеством параметров и типом взаимно соответствующих параметров.

Совместимость типов приобретает особое значение в оператора: присваивания. 


    Пусть T1 - тип переменной, а 72 - тип выражения, т.е. выполняется присваивание Т] .- Т2. Это присваивание возможно в еле дующих случаях:
. Т] и Т2 есть один и тот же тип и этот тип не относится к файла) или массивам файлов, или записям, содержащим поля-файлы, или массивам таких записей;
• Т1 и Т2 являются совместимыми порядковыми типами и значениями Т2 лежит в диапазоне возможных значений Т1;
• Т1 и Т2 являются вещественными типами и значение Т2 лежит Диапазоне возможных значений TI;
• ТУ - вещественный тип и Т2 - целый тип;
• TJ - строка и Т2 - символ;
• Т1 - строка и Т2 - упакованная строка;
• Т1 и Т2 - совместимые упакованные ñòðîêè;
• T1 и T2 - совместимые множества и все члены Т2 принадлежат множеству возможных значений Т1;
• T1 и Т2 - совместимые указатели;
• Т1 и Т2 - совместимые процедурные типы;
• Т1 - объект и Т2 - его потомок.
   В программе данные одного типа могут преобразовываться в данные другого типа. Такое преобразование может быть явным или неявным.
   При явном преобразовании типов используются вызовы специальных функций преобразования, аргументы которых принадлежат одному типу, а значение -другому. Таковыми являются уже рассмотренные функции ORD, TRUNC, ROUND, CHR. В гл. 6 описывается функций РТR, преобразующая четырехбайтный целочисленный аргумент к типу-указателю.
   В Турбо Паскале может использоваться и более общий механизм преобразования типов, согласно которому преобразование достигается применением идентификатора (имени) стандартного типа или типа, определенного пользователем, как идентификатора функции преобразования к выражению преобразуемого типа (так называемое автоопределенное преобразование типов). Например, допустимы следующие вызовы функций:
type 

  МуTуре = (а, b, c, d);
  MуТуре (2)
  Integer ( 'D')
  pointer ( longint(a) + $FF)
  char (127 mod с)
  byte (k)


   При автоопределенном преобразовании типа выражения может произойти изменение длины его внутреннего представления (длина может увеличиться или уменьшиться).
В Турбо Паскале определен еще один явный способ преобразования данных: в ту область памяти, которую занимает переменная некоторого типа, можно поместить значение выражения другого типа, если только длина внутреннего представления вновь размещаемого значения в точности равна длине внутреннего представления переменной. С этой целью вновь используется автоопределенная функция преобразования типов, но уже в левой части оператора присваивания:
type
  by1 = array [ 1. . 2 ] of byte; 
  int = array [ 1. .2 ] of integer; 
  RЕС = record
              x, у : Integer 
             end; 
var
  vbyt : byt; 
  vlnt : int; 
  vrec :rec; 
begin
  byt (vin1[1]) [2] := 0;
  int (vrec) [1] := 256
end.


   Неявное преобразование типов возможно только в двух случаях: 
• в выражениях, составленных из вещественных и целочисленных переменных, последние автоматически преобразуются к вещественному типу, и все выражение в целом приобретает вещественный тип;
• одна и та же область памяти попеременно трактуется как содержащая данные то одного, то другого типа (совмещение в памяти данных разного типа).
   Совмещение данных в памяти может произойти при использовании записей с вариантными полями (см. 4.2.2), типизированных указателей, содержащих одинаковый адрес (см. гл. 6), а также при явном размещении данных разного типа по одному и тому же абсолютному адресу. Для размещения переменной по нужному абсолютному адресу она описываете и с последующим зарезервированным словом ABSOLUTE, за которым помещается либо абсолютный адрес, либо идентификатор ранее определённой переменной. Абсолютный адрес указывается парой чисел типа WORD, разделенных двоеточием; первое число трактуется как сегмент, второе - как смещение адреса (см. гл. 6). Например:
b : byte absolute $0000:$0055;
w : Iongint absoIute 128 : 0 ;

   Если за словом ABSOLUТЕ указан идентификатор ранее определенной переменной, то происходит совмещение в памяти данных разного типа, причем первые байты внутреннего представления этих данных будет располагаться по одному и тому же абсолютному адресу, например:
var
  х : real;
  у : array [1..3] of Integer absolute x;

   В этом примере переменные X и У будут размещены, начиная с одного и того же абсолютного адреса. Таким образом, одну и ту же область памяти длиной 6 байт, а следовательно, и размещенные в этой области данные теперь можно рассматривать как данные либо типа REAL, либо как массив из трех данных типа INTEGER. Например, следующая программа выдаст на экран содержимое первых двух байт внутреннего представления вещественного числа П=3.1415 в виде целого числа:
var
  x : геаl ;
  у : array [1..3] of integer absolute x; 
begin
  x :=Pi; 
  writeln(y[1]) 
end. 

На экран будет выдан результат 8578.

      Вверх       оглавление


Хостинг от uCoz