Алгоритмический язык Паскаль

Общая характеристика языков программирования. Описание языка Паскаль: основные субъекты языка; структура Паскаль-программы; типизация и объявление данных. Операторы присваивания и выражения. Структурные операторы, организация ветвлений и циклов.

Рубрика Программирование, компьютеры и кибернетика
Вид дипломная работа
Язык русский
Дата добавления 26.01.2011
Размер файла 276,6 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

begin

¦ for K:= 1 to 6 do

¦ if K in D then write(K:4); writeln;

end;

begin

¦ A:= [1,3,4]; B:= [2,4,6]; C:= A + B;

¦ write('[1,3,4] + [2,4,6] ='); SRAWNENIE (C);

¦ C:= A - B;

¦ write('[1,3,4] - [2,4,6] ='); SRAWNENIE (C);

¦ C:= A * B;

¦ write('[1,3,4] * [2,4,6] ='); SRAWNENIE (C);

end.

ПОЯСНЕНИЕ. В программе определяются множества A, B, C типа BROSOK, элементами которых являются целые числа из диапазона [1..6], и процедура вывода на печать элементов таких множеств.

ЗАМЕЧАНИЕ 1. Если множество задано перечислимым типом, то его элементы напечатать нельзя. На печать можно вывести элементы только ординального типа: INTEGER, CHAR, BOOLEAN, интервальный.

ЗАМЕЧАНИЕ 2. Один и тот же набор данных можно организовать в виде линейного массива ARRAY, в виде множества SET и в виде строки типа STRING. Какой из этих видов предпочтительнее? Если над элементами (числами) производятся действия, то лучше ARRAY. Если же стоит задача о взаимосвязи элементов нескольких множеств или вопрос о вхождении каких-то объектов в множество, то лучше SET.

9. КОМБИНИРОВАННЫЙ ТИП - ЗАПИСИ. ДАННЫЕ ТИПА RECORD

Ранее было рассмотрено, как удобно работать с множествами и массивами. Однако все элементы множества всегда должны иметь один и тот же тип. Хотя в ряде случаев это вызывает определенные ограничения.

Рассмотрим в качестве примера задачу заполнения анкеты с некоторыми данными, например: имя, адрес, телефон, возраст, пол, семейное положение. Каждое из этих данных имеет свой тип. Однако все эти данные взаимосвязаны, они принадлежат всегда одному человеку, и хотелось бы, чтобы все они имели общее имя. Для таких случаев Паскаль предоставляет новый, комбинированный тип переменной, а именно RECORD - запись.

9.1 Определение типа RECORD

Так же, как и массив, запись объединяет переменные. Однако у записи переменные могут быть различных типов! Каждая компонента записи называется полем. Каждое поле записи имеет свой тип.

Мы уже знаем, что элементы массива всегда могут использоваться как отдельные переменные. Например, определив:

type RY = array [1..10] of integer;

var A: RY,

можно писать А[1],...,A[10]. Аналогичная ситуация имеет место и для записи. Здесь также можно использовать поля записи как отдельные переменные.

ПРИМЕР: type PATIENT = record

NAME: string [20];

MALADI: string [40];

AGE: integer;

MARIE: boolean;

end;

var NEKTO:PATIENT.

Это есть описание типа RECORD. Структура записи такого типа определяется здесь с помощью всех полей между RECORD и END.

В рассмотренном выше примере всей структуре этого типа присвоено имя PATIENT (пациент). Запись типа PATIENT состоит из четырех отдельных переменных, т.е. полей, которые имеют имена: NAME, MALADI, AGE, MARIE. Каждое из этих полей имеет свой тип. В разделе TYPE описывается тип PATIENT, который затем присваивается переменной NEKTO. Именно NEKTO есть переменная типа PATIENT, т.е. переменная типа RECORD.

Чтобы обратиться к некоторым полям записи, следует написать имя переменной и имя поля. Эти два идентификатора должна разделять точка.

ПРИМЕР:

NEKTO.NAME: = 'MANUELA'; NEKTO.AGE:= 20;

NEKTO.MALADI: = 'GRIP'; NEKTO.MARIE: = true.

Отметим, что поле записи, например поле NEKTO.AGE, может рассматриваться как обычная простая переменная целого типа:

NEKTO.AGE:= NEKTO.AGE + 1. Вместе с тем, запись может рассматриваться как единое целое. Пусть имеется следующее описание:

type DATE = record

DAY: 1...31;

MONTH: (JAN, FEB, MAR, APR, MAY, JUN, JUL,

AUG, SEP, OCT, NOV, DEC);

YEAR: integer;

end;

var HB, MB: DATE.

Мы видим, что HB и MB имеют тип DATE. Помимо действий над отдельными полями записей HB и МB можно выполнять операции над всей записью: HB:= MB.

Это присваивание эквивалентно следующей последовательности операторов: HB.DAY:= MB.DAY;

HB.MONTH: = MB.MONTH;

HB.YEAR:= MB.YEAR.

Для переменных этого типа вводятся сравнения: " = " и " <> ".

Так в нашем случае логическое выражение МB=HB является истинным.

Так как на тип компонент массива не накладывается ограничений, то можно образовывать массивы, компонентами которых являются записи. Например, вместо VAR NEKTO: PATIENT можно записать VAR NEKTO: ARRAY [1..N] OF PATIENT. Тогда фамилию первого пациента можно указать как NEKTO [1].NAME. Аналогично можно задать множество дат рождений N персон VAR BD: ARRAY[1..N] OF DATE.

Отсюда мы видим, что компоненты (элементы) массива BD есть записи. Чтобы обратиться к некоторому полю определенной записи массива, следует определить имя массива, индекс интересующей записи и имя необходимого поля. Например, для печати года рождения3-й персоны необходим оператор:

WRITELN (BD[3].YEAR).

Как и в случае с массивом, на типы компонент записи не накладывается каких-либо ограничений. Поля могут быть массивами, множествами или даже записями.

9.2 Оператор WITH

При работе с записями, при обращении к различным полям каждый раз приходится писать сначала имя самой записи, что не всегда удобно, если полей много. Поэтому в Паскале применяется оператор WITH:

with NEKTO do

begin

NAME:= 'MANUELA'; AGE:= 20;

MALADI:= 'GRIP';

MARIE:= true;

end.

Другими словами, в рамках оператора, помещенного внутри оператора WITH, к полям определенной переменной можно обращаться просто по имени (префикация имен опускается).

Особенно эффективно использовать WITH, когда речь идет о вложенных записях, т.е. таких, где поля есть тоже записи. Например, запись типа PATIENT можно расширить добавлением поля DATE, которое снова есть запись с 3-мя полями:

type PATIENT = record

NAME: string [10];

MALADI: string [30];

DATE: record

DEN: integer;

MESJATS: string [10];

GOD: integer;

end;

MARIE: boolean;

end;

var NEKTO: PATIENT.

При таком вложении доступ, например, к полю GOD уже должен сопровождаться указанием двух префиксных имен, например:

read (NEKTO.DATE.GOD).

Здесь уже WITH может значительно упростить работу с полями:

with NEKTO, DATE do

begin

NAME:= 'MANUELA'; AGE:= 20;

MALADI:= 'GRIP';

DEN:= 18;

MESJATS:= 'MART';

GOD:= 1944;

MARIE:= TRUE;

end.

Оператор WITH принято называть оператором присоединения. В общем случае он выглядит так: WITH R1, R2,..., Rn do S, что эквивалентно WITH R1 do WITH R2, R3,..., Rn do S.

Имя поля в операторе присоединения обозначает компоненту комбинированной переменной из ближайшего объединяющего оператора присоединения, в котором указана переменная с таким полем. Следовательно, если две переменные из списка комбинированных переменных оператора присоединения имеют поля, обозначенные одним и тем же именем, то внутри оператора WITH это имя обозначает поле той переменной, которая указана в списке позже.

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

Пусть, например, имеется простая переменная AGE и поле AGE некоторой комбинированной переменной NEKTO. В этом случае их можно отличить, т.к. простая переменная имеет имя AGE, а переменная-поле имеет полное имя NEKTO.AGE. А что будет в операторе WITH, где префикс NEKTO опускается?

В этом случае в операторе предпочтение отдается именам полей записи, т.е. считается, что внутри оператора WITH соответствующее имя обозначает имя поля, а не имя переменной.

Проиллюстрируем этот тезис на примере. Пусть даны типы:

const N_STUD =...;

N_SOTR =...;

n =...;

type SEX = (M,F);

STUD = RECORD

FAM,IM,OTH: array [1..N_STUD] of string[n];

POL: SEX;

GR: 111..154;

STIP: boolean;

end;

SOTR = record

FAM,IM,OTH: array [1..N_SOTR] of string[n];

POL: SEX;

DOLGN: (LAB, ASS, STPR, DOZ, PROF);

ZARPL: integer;

end;

var X: STUD; Y: SOTR;

STIP: integer;

Тогда можно дать такой фрагмент программы:

with X, Y do

begin

IM[5]:= 'ALEXANDR ';

POL:= M;

STIP:= true;

GR:= 122;

end;

STIP:= 160.

Здесь поля IM, POL относятся к переменной Y типа SOTR, т.к. эта переменная в списке переменных-записей заголовка WITH фигурирует после переменной X типа STUD. Кроме того, в этом фрагменте имя STIP в теле оператора WITH есть имя поля переменной Х.

9.3 Записи с вариантами

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

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

const MAXNOMBRE =...

type ENTRY = record

AUTOR, TITLE, PUBLISHER, SITY: STRING [100];

YEAR: 1...9999;

end;

var REFLIST: array [1...MAXNOMBRE] of ENTRY;

Здесь ENTRY - вход, т.е. данные о какой-либо научной работе. Если же некоторые работы входят в журналы, то нужно создавать новый массив данных только для журналов и работать с этими двумя массивами, что не очень удобно. В Паскале есть возможность образовать структуру с вариантами, каждый вход которой соответствует содержанию записи. Это достигается путем введения в описание записи специального оператора CASE- переключателя, который в чем-то похож на ранее введенный, но имеет свои синтаксические и семантические отличия.

В нашем примере, помимо описанного уже типа ENTRY, вводим еще один переменный тип:

ENTRYTYPE = (BOOK,MAGAZINE);

Теперь можно скорректировать раннюю запись:

type ENTRY = record

AUTOR, TITLE: string [100];

YEAR: 1..9999;

case TAG: ENTRYTYPE of

BOOK: (PUBLISHER, SITY: STRING [100]);

MAGAZINE: (MAGNAME: STRING; VOLUME, ISSUE: integer)

END;

Это описание делится на две части: фиксированную и вариантную. Поля: AUTOR, TITLE и YEAR - фиксированная часть. Остальная часть - вариантная, структура которой может меняться в пределах двух вариантов. Вариантная часть записи начинается со строки CASE, где в качестве селектора выступает не выражение, а идентификатор некоторого перечислимого типа. Элементы (компоненты) этого перечислимого типа (в нашем случае ENTRYTYPE) используются в качестве альтернативного определения записи: BOOK и MAGAZINE. В каждой альтернативе имеется свой набор полей:

BOOK: MAGAZINE:

AUTOR AUTOR

TITLE TITLE

YEAR YEAR

PUBLISHER MAGNAME

CITY VOLUME

ISSUE

Для того, чтобы различать, какую из ветвей нужно выбрать для работы, в такую запись вводится так называемое поле ТЕГА (tag fild) или узловое поле. Это дополнительное поле с именем TAG имеет тип ENTRYTYPE и помещается в качестве селектора в оператор CASE - OF:

ENTRY = record

AUT, TIT: string[100];

YEAR: 1..9999;

case TAG: ENTRYTYPE of

BOOK: (PUB,CYTY: string[100]);

MAGAZINE: (MAGNAME: string[100]; VOL,ISSU: integer);

end;

Здесь поле с именем TAG имеет тип ENTRYTYPE и принимает два значения. Если это поле имеет значение BOOK, то это ссылка на книгу, в противном случае - на журнал. Для определения составления записи с вариантами достаточно проверить значение поля TAG.

ПРИМЕР: Процедура печати значений записей типа ENTRY

procedure PRINTREF (CITATION: ENTRY);

begin

with CITATION do begin

writeln (AUTOR); writeln (TITLE); writeln (YEAR);

if TAG = BOOK then

writeln (PUB,',',CITY)

else

begin writeln (MAGNAME);

writeln (VOL,',',ISSUE)

end;

end;

end;

ЗАМЕЧАНИЯ:

1. Вариантная часть может содержать произвольное число аргументов, которые задействуются или перечислимыми типами, или произвольными порядковыми типами (интервалами).

2. Любая запись имеет только одну вариантную часть, которая должна всегда располагаться в конце описания, поэтому END оператора CASE совпадает с END всего описания.

3. Имя поля не может встречаться в двух вариантах одной записи.

4. В вариантной части могут встречаться другие новые вариантные части.

10. ФАЙЛОВЫЙ ТИП

До сих пор все рассмотренные типы переменных отличались тем, что в них заранее известно число компонент и тип этих компонент. Например, массив ARRAY[1..N] OF REAL состоит из N вещественных чисел, а запись:

record

POL1: string[M];

POLN: real;

end;

состоит из N полей, каждое из которых имеет свой тип. Кроме того, характерной особенностью всех рассмотренных ранее типов данных является то обстоятельство, что все эти данные неразрывно связаны с самим текстом программы и "живут" вместе с ней. Это означает, что все данные, присущие некоторой программе, не могут быть отделены от нее и использоваться в другой программе.

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

Итак, файл (FILE) представляет собой совокупность данных одинакового типа. В этом файл напоминает массив. Однако у массива с помощью индекса можно указать любой его элемент, например, A[7] - седьмой элемент. У файла же вызывать данные таким образом нельзя.

Условно файлы можно изобразить как некоторую ленту, у которой есть начало, а конец не фиксируется. Элементы файла записываются на эту ленту последовательно, друг за другом:

Файл напоминает магнитную ленту для записи мелодий, начало которой заполнено, а конец пока свободен. Новые записи помещаются в конец ленты. Прокрутить какую-то мелодию на ленте означает сделать протяжку ленты. Существует несколько разновидностей файлов, отличающихся методом доступа к ним. По способу доступа к элементам файла они бывают ПОСЛЕДОВАТЕЛЬНОГО и ПРЯМОГО доступа.

Файлы - это единственный тип данных, посредством которого данные получаются извне (входной файл) и передаются из ЭВМ во внешний мир (выходной файл). Файлы - средство связи с внешним миром.

10.1 Определение и описание файла

Файл представляет собой последовательность однотипных компонент произвольной длины. Каждый файл имеет свое имя, являющееся именем соответствующей файловой переменной, которая должна быть заявлена либо с помощью слова TYPE, либо - VAR. Для обозначения этого типа данных используется служебное слово FILE:

ПРИМЕР: a) type AZMORZE = (TOCHKA, TIRE);

MESSAGE = file of AZMORZE;

var TELEGRAM: MESSAGE;

б) var PISMO: file of char;

F: file of integer.

Здесь тип компонент может быть любым, кроме файлового типа.

Итак, в последнем примере определена F - переменная файлового типа. Это означает, что на ленте могут быть записаны только целые числа. Обратите внимание, что здесь никак не упоминается точное число элементов, которые можно записать в файл. Это можно делать постоянно, хотя в реальности лента, т.е. объем памяти, отведенной под запись файла, когда-то кончится (есть предел!!!).

10.2 Типы файлов. Процедуры работы с файлами

По своей связи с работающей программой файлы бывают внутренними и внешними.

ВНЕШНИЕ - это файлы, имена которых включены в список заголовка программы, и которые существуют вне программы, т.е. находятся на внешних носителях (дисках). Такие файлы заполнены заранее, записаны на дискету и могут быть использованы различными программами. Как уже сказано выше, их имена должны быть объявлены в заголовке программы: program OBRABOTKA (...,MESSAGE,PISMO,...).

ВНУТРЕННИЕ - это файлы, имена которых не внесены в заголовок программы. Они существуют только во время исполнения программы. Работа с ними идет одинаково, только внутренний файл пропадает после окончания работы программы.

Мы знаем, что каждый тип Паскаля имеет свой набор операций, определенный типом компонент этого объекта. Для внутренних файлов, рассматриваемых как единое целое, никаких операций нет - ни сравнения файлов, ни операции присваивания. Можно работать только с отдельными компонентами, и эта работа зависит от типа компонент файла. Для доступа к отдельным компонентам файла в Паскале введены стандартные процедуры RESET, GET, REWRITE, PUT и функции EOLN и EOF.

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

ЧТЕНИЕ - присваивание переменной значения компоненты файла;

ЗАПИСЬ - запись значения переменной в конец файла.

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

Для работы с файлами в режиме чтения и записи используются операторы REWRITE, WRITE, RESET, READ и EOF. Рассмотрим их синтаксис и назначение.

REWRITE(F)- установка в начальное положение режима записи.

F

^

окно

WRITE(F,X) - записывает в файл F (где сейчас стоит окно) очередную компоненту, равную значению выражения X, после чего окно сдвигается вправо на следующую позицию файла:

F

F1

F2

F3

F1

F2

F3

X

^

^

окно

окно

RESET(F) - перевод в режим чтения и установка окна на первую позицию файла.

F

^

окно

READ(F,V) - переменной V присваивается значение текущей позиции файла F, и окно перемещается на следующую позицию.

F

F1

F2

F3

F4

F

F1

F2

F3

F4

^

^

окно

окно

ПРИМЕЧАНИЕ. Файл открывается либо только для записи, либо для чтения - одновременно это делать нельзя!!!

При работе с файлами необходимо знать конец файла. Это делает логическая функция EOF:

EOF(F) = FАLSE, если конец файла не достигнут;

EOF(F) = TRUE - признак конца файла.

Функция EOF неразрывно связана с окном файла, которое всегда "смотрит" на одну из компонент файла. Эта функция всегда имеет определенное значение в зависимости от местонахождения окна файла. При заполнении файла последняя ее компонента всегда снабжается признаком конца. Пустой файл имеет единственную компоненту, содержащую признак конца файла.

ПРИМЕР 1. Расшифровка текста

Пусть все буквы от A до Z имеют коды от 65 до 90. Имеется файл SHIFRTEXT, состоящий из чисел от 65 до 90. Напечатать расшифрованный текст:

program RASSHIFROVKA (SHFRTXT);¦ program KODIROVKA;

type KOD = 65..90; ¦ type KOD = 65..90;

LITERA = 'A'..'Z'; ¦ SHIFR = file of KOD;

SHIFR = file of KOD; ¦ var x: KOD;

var x: KOD; ¦ SH: SHIFR;

y: LITERA; ¦ begin

SH: SHIFR; ¦ ¦ assign(SH,'shfrtxt');

begin ¦ ¦ rewrite (SH);

¦ assign(sh,'shfrtxt'); ¦ ¦ read(x);

¦ reset(SH); ¦ ¦ while x<> 00 do begin

¦ while not eof(SH) do¦ ¦ ¦ write (SH,x);

¦begin ¦ ¦ ¦

¦ ¦ read(SH,x); ¦ ¦ ¦ read(x);

¦ ¦ y:=chr(x); ¦ ¦ end;

¦ ¦ write(y); ¦ ¦ close(SH);

¦ end; close(sh); ¦ end.

end.

ПОЯСНЕНИЕ. В рассмотренном примере программа RASSHIFROVKA производит расшифровку строки числовых кодов файла SHFRTXT, сформированного с помощью программы KODIROVKA. В программе KODIROVKA предусмотрен непрерывный ввод кодов - двухзначных чисел от 65 до 90, разделяемых пробелом. Признаком конца ввода является код 00.

В обеих программах фигурируют операторы ASSIGN и CLOSE, о назначении которых речь пойдет в следующем пункте.

10.3 Буферная переменная

Мы знаем, что для чтения компоненты файла используется процедура READ(F,V). По этой процедуре выполняются два действия:

1. Копирование компоненты файла F, на которую смотрит окно, и присваивание этого значения переменной V.

2. Перемещение окна на следующую компоненту.

Однако, иногда удобно эти два действия разделить. Для этого вводится понятие буферной переменной. Она имеет имя: F^. Эту переменную не надо описывать, она определяется автоматически, как только описывается файл F. Тип F^ совпадает с типом компоненты файла. С переменной F^ можно выполнять все действия, как над данными типа компонент файла.

Если выполнена процедура RESET(F), то происходит установка окна на первую компоненту и значение этой компоненты идет в F^:

F

X

F^

^

окно

Если выполнен RESET(F), а файл F пуст, т.е. EOF(F)=TRUE, то значение F^ неопределенно. Окно файла указывает на его конец. Для передвижения окна файла и заполнения (чтения) буферной переменной используются в некоторых версиях Паскаля специальные процедуры-операторы GET и PUT:

а) GET(F) - передвижение окна на следующую компоненту и засылка значения этой компоненты в переменную F^.

F^

7

f

3

5

7

8

^

окно

после GET(F):

F^

8

f

3

5

7

8

^

окно

б) PUT(F) - запись в файл значения F^ и сдвиг вправо. Здесь до PUT(F) надо F^ присвоить очередное значение. В режиме записи значений в файл F^ служит поставщиком значений компонент. После процедуры REWRITE(F) окно устанавливается на первую компоненту, значение F^ не определено. Затем надо определить значение F^ с помощью команды присваивания: F^:=3. Если теперь написать процедуру PUT(F), то значение F^ идет в компоненту, где стоит окно, после чего значение F^ становится неопределенным:

F^

3

f

6

4

7

8

^

окно

PUT(F)

F^

f

6

4

7

8

3

^

окно

ПРИМЕР 2. Запись чисел v1 - v5 в файл BUFFER и последующий их вывод на печать

program KVADRKOREN;

var R: real; I: integer; BUFFER: file of real;

begin

¦ rewrite(BUFFER);

¦ for I:=1 to 5 do

¦ begin

¦ ¦ BUFFER^:=sqrt(I); put(BUFFER);

¦ end;

¦ reset(BUFFER);

¦ for I:=1 to 5 do

¦ begin

¦ ¦ R:=BUFFER^;

¦ ¦ get(BUFFER); writeln(R);

¦ end;

end.

ЗАМЕЧАНИЕ. В некоторых версиях процедуры GET и PUT не существуют (в частности, это имеет место для Турбо-Паскаля). Но без них можно обойтись, т.к. существуют эквивалентные им операторы READ и WRITE. Им эквивалентны следующие составные операторы:

READ(F,V) => BEGIN V:=F^; GET(F) END;

WRITE(S,W) => BEGIN S^:=W; PUT(S) END.

10.4 Основные приемы работы с файлами

Известно существование многих версий Паскаля, каждая из которых имеет свои особенности и отличия от стандарта Паскаля. Рассмотрим некоторые приемы работы с файлами в системах Turbo-Pascal для ПЭВМ "Ямаха" и IBM PC.

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

СИНТАКСИС: assign(var F: file; NAME: string), где NAME - имя файла на диске, F - имя файловой переменной.

После выполнения этой процедуры NAME и F отождествляются, например, ASSIGN(F,'nomfile') отождествляет файловую переменную F с его именем на диске. В качестве имени файла может быть указаноего полное имя, т.е. путь к этому файлу, например:

ASSIGN(F,'С:\WORK\ MIM\nomfile').

После окончания работы с файлом он должен быть закрыт процедурой CLOSE, иначе результат может быть потерян, т.к. директория не будет обновлена.

СИНТАКСИС: CLOSE(var F:file), где F - имя файловой переменной.

Процедуры ASSIGN и CLOSE взаимосвязаны и работают в паре друг с другом. Как уже сказано выше, перед началом работы с файлом выполняется процедура ASSIGN(F, 'nomfile'), которая для логического файла F готовит (ищет) на диске в указанной директории файл с именем NOMFILE. При окончании работы с файлом по выполнению процедуры CLOSE происходит его обновление (в случае записи) и закрытие (в случае чтения).

В программе надо уметь задавать исходные файлы. Эти файлы надо делать в цикле, используя при этом формирование компонент, либо в форме некоторого выражения по RANDOMIZE, либо задействовать обычную команду READ для ввода данных с клавиатуры. Цикл можно делать FOR, если формирование файла идет по RANDOMIZE, или WHILE (REPEAT), если файл формируется по признаку конца ввода.

Напомним, что RANDOMIZE - процедура инициализации генератора случайных величин; RANDOM - функция генерации случайных чисел.

Рассмотрим все эти особенности на примере формирования, обработки и вывода файлов.

ПРИМЕР 2. Для двух целочисленных файлов F и G одинаковой длины образовать третий целочисленный файл H, компоненты которого определяются по правилу: Hi=MAX{Fi,Gi}. В программе предусмотреть вывод на экран все трех файлов

program MAXELEM;

type FT = file of integer;

var F,G,H: FT;

I,J: integer;

procedure VIVODFILE(var A:FT);

begin

¦ reset(A);

¦ while not eof(A) do

¦ begin

¦ read(A,I); write(I:4);

¦ end; writeln;

end;

begin { формирование исходных файлов }

¦ assign(F,'F'); assign(G,'G');

¦ randomize; rewrite(F); rewrite(G);

¦ for I:=1 to 10 do

¦ begin

¦ J:= random(10)-5; write(F,J);

¦ ¦ J:= random(10)-5; write(G,J);

¦ end;

¦ VIVODFILE(F); close(F);

¦ VIVODFILE(G); close(G);

¦ assign(H,'H');

¦ { формирование файла результата }

¦ reset(F); reset(G); rewrite(H);

¦ while not eof(F) do

¦ begin

¦ ¦ read(F,I); read(G,J);

¦ ¦ if I > J then write(H,I) else write(H,J);

¦ end; VIVODFILE(H);

¦ close(H);

end.

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

procedure DOBAVLENIE(N: integer; var A:file);

var B: file; I,J: integer;

begin

¦ { Запись файла А в файл B }

¦ assign(B,'B');reset(A; rewrite(B);

¦ while not eof(A) do

¦ begin read(A,I); write(B,I); end;

¦ { Добавление новых элементов в файл B }

¦ for I:=1 to n do

¦ begin

¦ J:= random(10)-5; write(B,J);

¦ end;

¦ { Запись файла B в файл A }

¦ rewrite(A); reset(B);

¦ while not eof(B) do

¦ begin read(B,I); write(A,I); end;

end.

10.5 Текстовые файлы

Среди всех файлов особое место занимают текстовые файлы. Особенностью текстовых файлов является объединение в них символов в строки. Каждая строка кончается специальным символом конца строки. Этот специальный символ (литера) не входит в стандартный тип CHAR и не имеет графического представления. Нас и не интересует вид этого символа. Главное, что с ним связана логическая функция EOLN (конец строки). EOLN(F) = TRUE, если окно указывает на признак конца строки. Заметим, что если EOLN(F) = TRUE, то при чтении элементов из файла в символьную переменную она принимает значение пробела (пробел - аналог конца строки). Для записи в файл признака конца строки служит стандартная процедура WRITELN.

Текстовые файлы, т.е. файлы с делением на строки, описываются с помощью слова TEXT, например, VAR X, D: TEXT.

ПРИМЕР 3. Определить количество строк в файле с именем BOOK

program NOMBRELINE;

var K: integer; BOOK: text; S: char;

begin { Формирование файла BOOK }

¦ assign(BOOK,'f1'); rewrite(BOOK); read(S);

while S<> '.' do begin

¦ while S <> '#' do begin

¦ write(BOOK,S); read(S); end;

¦ writeln(book);read(S); end; close(BOOK);

¦ { Подсчет числа строк в текст; BOOK }

¦ K:= 0; reset(BOOK); writeln;

¦ while not eof(BOOK) do

¦ begin

¦ if eoln(BOOK) then K:=K+1; read(BOOK,S); write(S);

¦ end;

¦ writeln('В текстовом файле BOOK ', K,' - строк');

end.

ПОЯСНЕНИЕ. В программе сначала формируется текстовый файл, у которого строки кончаются символом "$", а сам текст - символом ".". Текст вводится с клавиатуры в виде непрерывной цепочки, например:

Наша Маша громко плачет,Уронила в речку мячик.$Тише, Машенька, не плачь,$Не утонет в речке мяч.$.

Во второй части программы с помощью функции EOLN подсчитывается число строк текста и он выводится на экран построчно, т.е. в виде:

Наша Маша громко плачет,

Уронила в речку мячик.

Тише, Машенька, не плачь,

Не утонет в речке мяч.

Итак, для записи литеры "конец строки" используется процедура WRITELN(F), где F находится в режиме записи.

T

a

g

c

d

^

окно

WRITELN(T):

T

a

g

c

d

#

^

окно

Таким образом сформированный файл легко выводится на печать построчно с помощью оператора WRITE, т.к. признак конца строки в файле иницирует переход на другую строку экрана (эффект оператора WRITELN).

В режиме чтения для работы с литерой "конец строки" есть процедура READLN. По этой процедуре окно файла устанавливается на первый элемент следующей строки файла.

T

d

b

c

d

#

e

f

^

окно

READLN(T):

T

d

b

c

d

#

e

f

^

окно

ПРИМЕР 4. Дано некоторое стихотворение в виде текстового файла ACROSTIH. Напечатать слово, образованное первыми буквами строк стихотворения (акростих)

program SLOVO(ACROSTIH); program FORMFIL;

var L:char; T: text; var F: text; S: char;

begin begin

¦ assign(T,'ACROSTIH'); ¦ assign(F,'ACROSTIH');

reset(T); ¦ rewrite(F); read(S);

¦ while not eof(T) do ¦ while S <> '?' do

¦ ¦ begin

¦ begin ¦ while S <> '#' do

¦ ¦ begin

read(T,L); write(L); ¦ write(F,S); read(S);

¦ ¦ end;

¦ readln(T); ¦ writeln(F);read(S); end;

¦ end; ¦ close(F);

end. end.

ПОЯСНЕНИЕ. Программа FORMFIL формирует текстовый файл ACROSTIH как было показано в примере 3. В программе SLOVO файл ACROSTIH выступает как внешний. Ему соответствует файловая переменная T. Оператор READLN(T) последовательно устанавливает окно файла на начало строк текста.

Файлы, как переменные величины, могут выступать в качестве аргументов и результатов при создании функций-процедур, причем эти переменные должны быть всегда оформлены как параметры-переменные, даже если файл в процедуре играет роль аргумента.

ПРИМЕР 5. Посчитать число знаков препинания в указанном текстовом файле

function PUNCTUATION(var CHARFILE: text): integer;

var SYMBOLNOMB: integer;

SYMBOL: char;

begin

SYMBOLNOMB:=0; reset(CHARFILE);

while not eof(CHARFILE) do

begin

read(CHARFILE, SYMBOL);

if SYMBOL in ['.',',',' ',':','...] then

SYMBOLNOMB:= SYMBOLNOMB + 1

end;

PUNCTUATIОN:= SYMBOLNOMB

end.

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

assign(FIL,'FIL');

reset(FIL);

n:=PUNCTUATION(FIL);

close(FIL);

writeln('число знаков препинания в тексте FIL =', n).

11. ССЫЛОЧНЫЙ ТИП. ПЕРЕМЕННЫЕ С УКАЗАТЕЛЯМИ

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

НАПРИМЕР:

а) с помощью описания VAR A, B: INTEGER в программе вводятся в употребление две статические переменные с именами А и В, значениями которых будут целые числа;

б) описание VAR X: ARRAY[1..10] OF REAL oпределяет (порождает) переменную регулярного типа (массив), значением которой может быть упорядоченная последовательность из десяти вещественных чисел.

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

При трансляции Паскаль-программа превращается в программу на машинном языке (в цифровых кодах), где аналогом переменной является ячейка, а имя переменной превращается в адрес ячейки. Появление этого адреса происходит в результате работы специального оператора языка (NEW), однако его значение в большинстве случаев не используется при программировании на алгоритмических языках типа Паскаль.

Условимся считать, что адрес ячейки, которая будет хранить переменную А, есть А. Или, другими словами, А - это общее имя переменной и соответствующей ячейки памяти.

Так, например, при описании VAR A, B: INTEGER в памяти ЭВМ резервируются две ячейки, которые маркируются соответственно А и В.

Будем считать, что до начала работы программы эти ячейки пусты (на самом деле они содержат "мусор" или 0). Если теперь в программе переменным А и В присвоить значения А:= 1 и В:= 2, то эти ячейки заполнятся соответствующими данными значениями.

При описании VAR X: ARRAY[1..10] OF REAL в памяти ЭВМ резервируются подряд 10 ячеек памяти, которые идентифицируются соответственно X[1], X[2],..., X[10].

Если теперь сделать

FOR I:= 1 TO 10 DO READ(X[I]),

то эти ячейки заполнятся десятью числами, вводимыми с клавиатуры. При упоминании в программе имен А и В фактически указывается на содержимое ячеек А и В, т.е. на значение переменных. Так, если следует оператор WRITE(A), то печатается не А, а значение переменной А, т.е. число 2.

Из этих рассуждений следует, что для таких переменных (статических) область памяти закрепляется на все время работы программы. Поэтому ячейка с адресом А всегда будет хранить только целые числа, а группа ячеек с адресами Х[1],...,X[10] - 10 вещественных чисел. Ничто другое в эти части памяти не может быть помещено.

Однако такое постоянное распределение памяти удобно для быстродействия и хорошо, когда объемы информации невелики или заранееизвестны. На практике же бывают ситуации, когда программные объекты могут возникнуть только в процессе выполнения программы или когда такие объекты известны, но их размер определится только в процессе работы программы.

Мы уже сталкивались с такой ситуацией:

CONST N =.....

VAR X: ARRAY [1..N];

или

VAR Y: STRING [N].

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

1. Пусть в заданном тексте из слов произвольной длины требуется найти первое по порядку слово, которое обладает некоторым свойством (не содержит, например, букву "а").

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

2. Бывает, что какой-то программный объект (например, массив чисел, множество, список) нужен не на все время работы программы, а только на какую-то часть. Хотелось бы после отработки данного объекта разместить на этом месте памяти другой объект.

11.1 Определение ссылочного типа

Для работы с динамическими объектами статические переменные не подойдут, здесь предусматривается специальный тип значений - ссылочный. Значением данного типа является ссылка на какой-либо программный объект, осуществляющая непосредственный доступ к этому объекту. На машинном языке такая ссылка указывает на место памяти, т.е. адрес соответствующего объекта. А так как ссылочная переменная есть переменная, то возможно изменение значения ссылок и одна и та же переменная может указывать на любую ячейку памяти.

Итак, теперь нам предстоит иметь дело с переменными (а значит, с ячейками), которые обладают именами, но их содержимым является адрес ячейки памяти, где хранится значение некоторой другой переменной:

Имя ячейки

Такие переменные называются переменными типа указатель (переменными ссылочного типа) или просто указателями (ссылками).

Итак, слова - синонимы:

Значение указателя есть адрес объекта (ссылка на объект), посредством которого он и доступен в программе.

Итак, указываемый объект есть динамический объект. Он хранится в ячейке, которая не имеет своего собственного имени (не обозначается именем переменной), а используется лишь ссылка на эту ячейку. Здесь для сравнения можно привести ситуацию, когда называют зрителя в зале театра: "Зритель, сидящий на 3-м месте в 5-м ряду".

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

Как задать ссылочный тип, т.е. как описать указатель? Указатель обозначают обычным идентификатором. О том, что это указатель, говорит присутствие символа "^".

Значениями переменных такого ссылочного типа являются ссылки на динамические объекты, а сами динамические объекты имеют указанный в описании тип.

НАПРИМЕР:

TYPE MAS = ARRAY [1..100] OF INTEGER;

DINMAS = ^MAS;

VAR P: ^INTEGER; Q: ^CHAR;

RABMAS: DINMAS.

ЗДЕСЬ: P - ссылка на динамический объект целого типа, Q - ссылка на динамический объект литерного типа, RABMAS - ссылка на динамический объект, значением которого является массив из 100 чисел.

11.2 Создание динамических переменных. Процедура NEW

Описание переменных P и Q ссылочного типа в разделе объявлений еще не резервирует память для записи значений динамической переменной соответствующего типа.

Здесь вводятся в употребление статические переменные P и Q (транслятор резервирует место в памяти, необходимое для размещения ссылки). Пустые клетки в схеме означают, что пока переменные P и Q не имеют никаких ссылок на динамические объекты.

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

Это процедура с параметром - ссылочной переменной, сопоставленной порождаемому динамическому объекту.

Итак, по процедуре NEW (R) выполняется резервирование участка в определенном месте памяти для последующего размещения значений динамической переменной и помещение адреса этого участка в ссылочную переменную R. При этом выделяется столько ячеек памяти, сколько требует значение динамической переменной, на которую указывает R. Количество отводимых ячеек зависит от типа динамической переменной. Динамические переменные, созданные посредством NEW, называют указанными переменными.

ЗАМЕЧАНИЕ. Резервирование места для динамической переменной идет уже в ходе выполнения программы, а не при ее трансляции, как для статических переменных. Говорят, что NEW устанавливает водораздел между статическими и динамическими переменными.

КАРТА ПАМЯТИ

MSX-DOS

Библиотека PASCAL

TURBO-система

Исходный текст программы

Объектный ход программы

Указатель

-----------------

Куча

ПРИМЕЧАНИЕ. Здесь указана примерная схема карты памяти при работе системы Турбо-Паскаль.

11.3 Переменные с указателями

При использовании в программе ссылочных переменных естественно возникает вопрос, как работать с динамическим объектом, т.е. как присваивать ему то или иное значение и распоряжаться им? Другими словами, как в программе на языке Паскаль ссылаться на динамический объект? Ведь мы знаем, что динамическим переменным, в отличие от статических, не дается имени в обычном понимании. Поэтому для работы с динамическим объектом в языке используется понятие "переменная с указателем".

Имя динамической переменной складывается из имени статической переменной типа указатель, поставленной в программе в соответствие данному динамическому объекту, и символа "^" после ссылочной переменной, свидетельствующего о том, что здесь речь идет не о ее значении, а о значении того программного объекта, на который эта ссылочная переменная указывает.

НАПРИМЕР:

Р - указатель: 5 ряд, 6 место,

Р^ - человек, сидящий в 5 ряду на 6 месте;

или

А - указатель,

А^ - имя динамической переменной, на которую указывает А.

Короче, А^ - переменная "старого" типа, которой можно присваивать конкретные значения.

Итак, пусть в программе содержатся:

VAR P: ^INTEGER; Q: ^CHAR;

NEW (P); NEW (Q).

Теперь в программе порождаются динамические переменные типов INTEGER и CHAR, которым с помощью оператора присваивания можно давать конкретные значения: Р^:= 58; Q^:= 'a'.

Переменная с указателями (она синтаксически играет роль динамической переменной) используется в любых конструкциях языка как обычная переменная, в зависимости от ее типа. Так, если R есть тоже переменная типа INTEGER, то синтаксически правильно написание следующих операторов присваивания:

R:= R + P^ + 2; P^:= P^ div 3.

В качестве ссылочной переменной может использоваться и более сложная конструкция, являющаяся частичной переменной, имеющей соответствующий ссылочный тип. Так, если в программе есть описания ссылочного типа TYPE REFREAL = ^REAL и переменной этого типа VAR A: ARRAY[1..50] OF REFREAL (в силу которого значением переменной А может быть массив элементов ссылочного типа, причем каждая из ссылок указывает на вещественное значение), то в качестве ссылочной переменной может фигурировать переменная с индексом, например, А[2]^ или А[K+5]^. Значением этих переменных с указателями будут вещественные числа. Иначе, определив

TYPE P = ARRAY[1..50] OF INTEGER; VAR B: ^P,

переменная В будет указателем на массив типа Р и в этом случае элементы массива обозначаются так - В^[2], B^[K+5].

ПРИМЕР 1. Поиск буквы во множестве букв и печать общих букв двух множеств

programm SETUK;

type MN = set of char;

var A,B: ^MN; C: MN; I,D: char;

begin

¦ new(A); A^:= ['a','c','o'];

¦ write('введите букву, которую надо найти:'); rеаdln(d);

¦ if D in A^ then writeln('да')

¦ else writeln('нет');

¦ new(B); B^:= ['g','c','o']; C:= A^ * B^;

¦ writeln('общие буквы множеств:');

¦ for I:= 'a' to 'z' do

¦ if I in C then write (I,' ')

end.

ПОЯСНЕНИЕ. В этой программе используются два динамических множества А и В, а также статическое множество С. Место, занимаемое в памяти под запись множеств A и B может быть освобождено после получения множества C.

ВЫВОДЫ (отличия динамической переменной от статической):

Вместо описания самих динамических переменных в программе дается описание указателей, поставленных в соответствие с ними.

В определенном месте программы должно быть предусмотрено порождение каждой из динамических переменных с помощью процедуры NEW.

Для идентификации значения динамической переменной используется переменная с указателем.

11.4 Операции над указателями

Значение одного указателя можно присваивать другому указателю того же типа.

Пусть объявлены переменные VAR Q, R: ^INTEGER и указатель R содержит адрес динамической переменной, значение которой равно 1, а Q - адрес динамической переменной, значение которой равно 2:

NEW(Q); NEW(R); Q^:= 2; R^:= 1.

Тогда оператор Q:= R перешлет в Q тот же адрес, что хранится в R, т.е. теперь Q будет "показывать" на то же значение (ту же ячейку памяти), что и R, а значение, на которое показывало Q раньше, будет навсегда утеряно. Этот процесс представлен схемой:

VAR Q,R:^INTEGER;

R

Q

NEW(R);

R

*

R^

NEW(Q);

Q

*

Q^

R^:=1;

R

*

1

R^

Q^:=2;

Q

*

2

Q^

Q:=R

R

*

1

R^ или Q^

Q

*

2

Потеряно

Можно присваивать значения одной динамической переменной другой, но того же типа. В этом случае значения обеих динамических переменных становятся равными, но значения указателей при этом не изменяются. Например, выполнена команда присваивания Q^:= R^, в результате получим одинаковое значение у двух переменных, как это показано на схеме:

До

После

R

*

1

R

*

1

Q

*

2

Q

*

1

Обмен значениями динамических переменных не изменяет значения ссылочных переменных. В этом случае, в отличие от оператора Q:=R, после выполнения которого Q и R "смотрят" на одну и ту же динамическую переменную, содержащую 1, каждая переменная Q и R указывает на свою динамическую переменную, хотя они обе содержат 1.

ЗАМЕЧАНИЕ. Следует различать пустой и неопределенный указатели. Так, при объявлении с помощью VAR некоторой переменной P (VAR P: ^INTEGER), ее значение является неопределенным. Если же имеет место оператор NEW(P), то ссылочная переменная получает свое конкретное значение - адрес ячейки памяти соответствующей динамической переменной P^. Переменная P может получить значение без оператора NEW только в случае присваивания пустой ссылки P:=NIL или ссылки, уже ранее получившей свое значение с помощью оператора NEW.

Итак, никакие действия со ссылочными переменными нельзя производить до действия оператора NEW. В примере

VAR I,J: ^ INTEGER;

NEW (I);

оператор I:= J не законен, т.к. J еще не определена, но допустим оператор J:=I.

ПРИМЕР 2. Подсчет числа вхождений заданной буквы в первое по порядку слово максимальной длины из заданного текста, заканчивающегося точкой

program FINDLITER;

type MAS = array [1..100] of string[1];

LINK = ^MAS;

var R, REZSLOVO, TEKSLOVO: LINK;

max,i,k,j: integer;

S: string[100]; BUKWA: string[1];

begin

¦ max:= -1; i:= 0; new(TEKSLOVO); new(REZSLOVO);

¦ writeln('Введите текст: '); readln(S);

repeat

¦ for j:= 1 to length(S) do

¦ begin

¦ ¦ BUKWA:= copy(S,j,1);

¦ ¦ if (BUKWA <> ' ') and (BUKWA <> '.')

¦ then begin i:= i+1;

¦ ¦ TEKSLOVO^[i]:= BUKWA; end

¦ ¦ else if i > max then

¦ ¦ begin max:= i; R:= REZSLOVO;

¦ ¦ REZSLOVO:= TEKSLOVO;

¦ ¦ TEKSLOVO:= R; end;

¦ ¦ i:=0;

¦ end;

¦ until BUKWA = '.';

¦ writeln('Введите букву: '); read(BUKWA); k:= 0;

¦ for i:= 1 to max do

¦ if BUKWA = REZSLOVO^[i] then k:= k+1;

¦ writeln;

¦ write(' В слово максимальной длины: ');

¦ for j:= 1 to max do write(REZSLOVO^[j]); writeln;

¦ writeln (' буква ',BUKWA,' входит ', k,' раз');

end.

Заметим также в заключение, что ссылки, которые указывают на идентичные типы, можно сравнивать друг с другом с помощью знаков "=" и "< >", при этом P = Q, если:

а) P = NIL, Q = NIL;

b) P и Q указывают на один и тот же динамический объект.

11.5 Действия над динамическими переменными

Мы рассмотрели вопрос, когда речь шла о действиях над ссылочными переменными (указателями). Теперь обратимся к действиям над динамическими переменными.

Как уже говорилось выше, динамические переменные призваны более рационально использовать память в процессе работы программы. Рациональность заключается, прежде всего, в том, чтобы убирать из памяти уже ненужные данные. Это достигается с помощью оператора DISPOSE, который имеет вид DISPOSE (R), где DISPOSE - имя процедуры стирания, а R - имя ссылочной переменной, указывающей на динамическую переменную R^, подлежащую удалению.

Итак, DISPOSE освобождает память для нового использования. Динамические переменные, не стертые с помощью DISPOSE, продолжают занимать место в "куче" после окончания работы фрагмента программы (становятся "мусором"), их надо убирать.

ПРИМЕР 3. Порождение и последующее стирание двух динамических объектов

program UKAZATEL;

var A,B: ^integer;

K: integer;

begin

new(A); new(B); A^:= 1; B^:= 2;

K:= A^ + B^; write(K);

dispose(B); dispose(A);

end.

ПРИМЕР 4. Нахождение в вещественном массиве RA элемента с индексом, равным значению наименьшего элемента массива IA

program MINPOISK;

type RA = array[1..10] of real;

IA = array[1..10] of integer;

PR = ^RA; PI = ^IA;

var k,i: integer;

F: PR;

G: PI;

begin

{порождение динамического массива IA, поиск в нем наименьшего элемента с последующим уничтожением}

new(G); randomize;

G^[1]:= random(12) + 6; k:= G^[1];

for i:= 2 to 10 do begin G^[i]:= random(12) + 6;

rite(G^[i],' '); if G^[i] < k then k:= G^[i] end; writeln;

writeln('Вот значение искомого индекса = ', k); dispose(G);

{порождение динамического массива RA, поиск в нем k-го элемента}

new(f); for i:= 1 to 10 do

begin F^[i]:= random(12);

write(F^[i]:5:1) end; writeln;

writeln('Искомый элемент равен ',F^[k]:5:1);

end.

ПРИМЕР 5. Нахождение наибольшего из чисел, на которые ссылаются элементы массива указателей

program MZB;

const n = 10;

type S = ^real;

W = array[1..n] of S;

var X: W; i: integer; k: real;

begin

¦ {Порождение динамического массива }

¦ new(X[1]); X[1]^:= random(10) + 4;

¦ write (x[1]^:4:1,' '); k:= X[1]^;

¦ { Поиск наибольшего элемента }

¦ for i:= 2 to n do

¦ begin

¦ ¦ new (X[i]); X[i]^:= random(10) + 4;

¦ ¦ write (X[i]^:4:1,' ');

¦ ¦ if X[i]^ > k then k:= X[i]^;

¦ end;

¦ writeln; writeln ('Наибольший элемент: ', k:4:1);

¦ { Уничтожение сформированного массива }

¦ for i:= n downto 1 do dispose (X[i]);

end

ЗАМЕЧАНИЕ. В отличие от примера 4, где были образованы массивы, на которые указывали соответствующие ссылки (одна ссылка на весь массив), здесь весь массив состоит из ссылок, каждой из которых соответствует свой элемент порождаемого с помощью RANDOM массива. Поэтому для уничтожения массива примера 4 понадобился всего один оператор DISPOSE, а в примере 5 уже требуется уничтожать каждый элемент массива в отдельности.

11.6 Динамические структуры данных. Обработка цепочек

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

Чаще всего переменные этих типов используются самостоятельно (обособленно), однако можно создавать на их основе другие типы данных более высокого уровня сложности, которые принято называть комбинированными. Мы уже знаем пример такого типа - RECORD, позволяющего объединить в единое целое данные практически всех указанных выше типов (поля записи имеют разный тип данных).

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

С понятием "цепочка" связано понятие строки - упорядоченной последовательности данных алфавита языка. Строка - самая универсальная структура данных, с ней связаны решения многих задач.

Есть три классические задачи работы со строками:

1) поиск вхождения заданной литеры в строку;

2) вставка заданной литеры в указанное место строки;

3) исключение литеры из указанного места строки.

Решение этих задач зависит от способа представления строки:

1) векторное представление;

2) представление строки в виде цепочки с использованием ссылочного и комбинированного типов.

Сюда относятся типы STRING и символьный массив. Например, слово PASCAL можно представить двумя способами:

VAR S1: STRING[6]; S2: ARRAY[1..6] OF CHAR.

В этом случае S1[1]='P', S2[4]='C'.

Итак, мы имеем непосредственный доступ к литере, и это удобно для решения первой задачи. Сложнее обстоит вопрос с решением задачи вставки элемента в строку (или удаления из строки).

Например, в слово PASAL нужно вставить пропущенную букву C PASCAL. Здесь элементы, идущие за вставкой, увеличивают свои индексы, т.е. после вставки надо проводить переиндексацию программным путем. Такая же ситуация и при удалении элемента.

При вставке и при удалении длина строки меняется. Следовательно, нужно заказывать длину объекта чуть больше реального и предусматривать указатель конца строки, например, знак "#".

ПРИМЕР 6. Удаление в литерном векторе элемента, следующего за указанным индексом

program UDAL;

const N =...

var ST: array[1..N] of char;

K, IND: integer;

begin {ввод строки, последний элемент = '#'}

¦...................

¦ writeln('индекс?'); read(IND); K:=IND+1;

¦ while ST[K]<>'#' do

¦ begin ST[K]:=ST[K+1]; K:=K+1; end;

end.

Эта задача решается гораздо проще, если представить литерный вектор с помощью типа STRING и применить процедуру DELETE, однако и здесь надо заранее заказывать длину вектора.

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


Подобные документы

  • Основные сведения о системе программирования Турбо Паскаль. Структура программы на Паскале и ее компоненты. Особенности и элементы языка Турбо Паскаль. Порядок выполнения операций в арифметическом выражении, стандартные функции и оператор присваивания.

    лекция [55,7 K], добавлен 21.05.2009

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

    курсовая работа [290,9 K], добавлен 05.12.2008

  • Алгоритмы и алфавит языка Турбо Паскаль. Основные типы данных. Операторы присваивания, перехода и выбора. Понятие массива в Паскале. Особенности работы со строками в программе. Использование линейного поиска и поиска с барьером. Основные виды сортировок.

    учебное пособие [53,2 K], добавлен 09.11.2009

  • Общая характеристика языка программирования Турбо Паскаль: операторы, циклы, файлы. Процедуры и функции модуля Crt. Структурная и функциональная схема программы учета учащихся, таблица идентификаторов. Список и описание использованных подпрограмм.

    курсовая работа [702,9 K], добавлен 29.01.2011

  • История и основы структурного программирования в среде Turbo Pascal. Работа с различными типами данных. Операторы языка. Работа с символьными и строковыми переменами, одномерным, двумерным массивами. Классификация компьютерных игр. Игры на языке Паскаль.

    курсовая работа [28,8 K], добавлен 06.05.2014

  • Сравнительный анализ языков программирования высокого уровня Си и Паскаль: структура программы, типы данных, арифметические операции, операторы ветвления и циклы. Создание программы поиска подпоследовательностей одинаковых элементов в множественном виде.

    курсовая работа [78,9 K], добавлен 28.12.2012

  • Описание конструкций языка программирования Паскаль, обеспечивающих ветвление. Организация циклических процессов. Создание программы для ввода последовательности вещественных чисел до появления 0, расчет среднего арифметического данной последовательности.

    лабораторная работа [189,8 K], добавлен 17.04.2012

  • Разработка консольного приложения, которое обрабатывает входной файл с расширением .pas и транслирует в выходной файл с расширением .cpp или .txt (по выбору пользователя). Синонимичные операторы языков Паскаль и С. Разработка алгоритма решения задачи.

    курсовая работа [329,6 K], добавлен 04.06.2013

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

    курсовая работа [305,9 K], добавлен 03.07.2011

  • Основы создания фрактальных композиций при помощи среды программирования Паскаль. Сущность и основа фрактальной графики. Графические возможности и операторы для рисования геометрических фигур в среде Паскаль. Примеры обозначения цветов на языке Паскаль.

    лабораторная работа [15,7 K], добавлен 12.06.2010

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.