close

Вход

Забыли?

вход по аккаунту

код для вставкиСкачать
Введение
В первые описание языка Паскаль появилось в 1968 году. Автором языка является Вирт, бывший в то время профессором Высшей
технической школы г. Цюриха. Язык создавался, как учебный, чтобы научить студентов писать технологичные программы. Язык назван
в честь французского математика XVIII века Блеза Паскаля. Спустя два года появился первый рабочий транслятор. Интерес к языку
возник и в практической сфере , о чем свидетельствовало появление на различных типах ЭВМ трансляторов. Но, поскольку, не было
стандарта, то каждый транслятор воспринимал свой диалект языка. К тому же практическое использование языка потребовало внести
небольшие изменения. Все это привело к тому, что в 1973 году появилось “Пересмотренное сообщение”, в котором Паскаль
описывается в терминах международного стандарта( ISO ). Но только в 1982 году был опубликован международный стандарт языка
Паскаль.
Паскаль можно сравнить с некоторыми другими языками по следующей схеме:
1. Структура данных (типы данных языка);
2. Управление данными (преобразование типов данных);
3. Структура памяти (как хранятся типы данных, виды распределения памяти для данных);
4. Управление памятью ( как выделяется память; на каком этапе трансляции,
выполнения);
5.Структура действий ( арифметические ,функции ,ветвления ,циклы);
6. Управление действиями (соответствующие конструкции и их семантика);
7. Операционная среда (какие средства языка задевает операционная среда, точность представления чисел, ограничения);
8.Управление операционной средой ( как реализован ввод-вывод, действия по прерыванию, возможность выйти из одного языка и
войти в другой).
Таким образом, схему можно представить:
Язык - стандарт
Реализация
структур
Структура - управление
Реализация
управления
Данные
Данные
Данные
Память
Память
Память
Действия
Действия
Опер.
Среда
Действия
Операционная среда
Опер.
среда
Реализация языка программирования формирует диалект языка. Если используется диалект языка таким образом, что в него
входят только стандартные компоненты,
стандарт
диалект
то проблем с переносом программ на другую машину не возникает; иначе, те компоненты языка, присущие только диалекту,
реализованному на машине X, придется переделать таким образом, чтобы программа могла работать на машине Y, т. е.
программу придется переписывать на диалект машины Y.
Во многих руководствах языки программирования описаны так, что более или менее полно раскрыты такие части языка как
структура данных и управление данными , а также структура действий и управление действиями. Очень мало или почти нет
информации о структуре памяти и об управлении памятью, а также об операционной среде и управлении операционной средой.
Объясняется это тем, что многие авторы относят четыре последних вопроса к реализации языка.
Рассмотрим задачу, решение которой позволяет продемонстрировать основные понятия языка.
Задача № 1.
Ввести с терминала целое число и действительное. Увеличить первое на 5, а второе уменьшить в три раза. Вывести на терминал
исходные числа и результаты.
Решение.
При разработке алгоритма решения задачи воспользуемся методом “Разбиение задачи на подзадачи” или “ Пошаговой
детализации”:
1. Ввести с терминала первое число;
2. Увеличить его на 5;
3. Вывести полученный результат на терминал;
(1)
4. Ввести с терминала второе число;
5. Разделить его на 3;
6. Вывести полученное частное на терминал.
Не важно, в какой последовательности выделены подзадачи, главное то, что подобная последовательность решает задачу.
По правилам программа на языке Паскаль выглядит так :
program имя_программы ( список_имен_файлов);
Описания
begin
Предложения языка Паскаль
end.
Любой язык программирования обладает:
синтаксисом - правила записи программы ( начинается с оператора program ... и заканчивается end. ) ,
семантикой - смысл выполнения операторов программы,
прагматикой - отношение программы к реальности.
1.Разберем заголовок программы:
1. Выделено служебное слово - program;
2. Имя_программы - это элемент синтаксиса заголовка программы. С точки зрения синтаксиса программы - это идентификатор.
Идентификатор - последовательность латинских букв и цифр начинающаяся с буквы. С точки зрения семантики имя - это
название ( например Петя). Прагматика имени может быть или нет, в зависимости от осмысленного названия.
2.Между служебными словами begin и end находятся выполняемые операторы языка Паскаль.
3.Точка в программе ставится только один раз - в конце программы.
Исходная программа будет выглядеть так:
program ExampleOne (входной_файл, выходной_файл);
описание_переменных_и_файлов
begin
(1)
end.
Рассмотрим вторую строку программы. В ней дается описание переменных, которые связаны с понятием данные. В Паскале есть два
вида объектов, с которыми манипулирует пользователь: данные и действия.
Форма представления информации в виде 0 и 1 для обработки ЭВМ - называется данными.
Данные представлены константами и переменными, действия - операциями и операторами. Ясно, что действия необходимы, чтобы
преобразовать данные, которые обладают тремя характеристиками: значением, типом значения и именем. Значение - это смысл
данного. Тип значения - это множество, которому принадлежит значение. Имя - это обозначение данного в программе. Стандарт
определяет имя как идентификатор. Длина его не оговорена. Примеры имен: TERMIN, FileName, A100.
Арифметические типы
Язык Паскаль является языком с сильной системой типизации. Это означает, что все данные, используемые в программе, должны
быть отнесены к определенному типу. Число типов не фиксировано. Все множество типов делится на два класса: предопределенные и
определяемые программистом. Рассмотрим два предопределенных множества: INTEGER и REAL. Это служебные слова, которые
нельзя использовать нигде кроме определения типа переменных. Множество INTEGER - это подмножество целых чисел. В стандарте
оно определено в диапазоне -MAXINT-1 : MAXINT. Множество INTEGER замкнуто относительно операций: +, -, *, если результат не
превышает MAXINT. Операция деления двух целых чисел дает действительное число (REAL).
Например :
5/2=2.5 - real; 5+2=7, 5-2=3, 5*2=10 - Integer.
Определены две операции целочисленного деления:
1. Деление нацело a div b => c, a, b -integer;
2. Деление с остатком a mod b => d.
Например: 5 div 2 = 2, 5 mod 2 = 1.
Div и mod нельзя использовать в качестве имен.
Будьте очень внимательными, когда результат действий над целыми числами близок по модулю к MAXINT.
Множество REAL - подмножество реальных чисел, которое определяется реализацией языка, точнее диалектом языка. Следует
заметить, что если бы числа в ЭВМ хранились в десятичной системе счисления , то чисел от 1 до 10 было бы столько, сколько от 1000
до 10 000 ( т.к. числа хранятся в нормализованном виде), т.е. чем длиннее интервал , тем реже числовые данные типа REAL . В
Паскале операции +, - ,*, / замкнуты относительно множества REAL. Операции возведения в степень в Паскале нет.
Числовые данные в программе представлены:
константами - ?
переменными - известны
выражениями - ?
Целочисленные константы имеют естественную форму записи:
-5, +3 , 0 , 035 и т.д.
Вещественные константы имеют две формы записи:
1) -356.0 , +23.77 и т.д. Не допускаются такие формы записи : -.03 , -10. . Дробная и целая часть числа обязательны, если
присутствует десятичная точка.
2) с плавающей точкой -356.0Е-4 => 0.0356, +23.23E40 => 23230 ...0.
Количество цифр в мантиссе не более 12. Напомним, что числа в памяти ЭВМ представлены в нормализованном виде с
выравниванием порядка таким образом, что целая часть числа равна нулю, а дробная называется мантиссой.
Выражения.
Арифметическое выражение в Паскале - это обыкновенное арифметическое выражение, в котором данные представлены
константами и переменными, а действия операциями +,-,*, /, div , mod.
Порядок вычисления выражений естественный. Если нужно его изменить , то применяют “(”, “)”. Разрешается использовать в одном
выражении данные типа INTEGER и REAL , но надо помнить условие:
1) если в a op b- а, b - INTEGER и op - +, -, *, div , mod , то значение
a op b - INTEGER;
2) если в a op b - a, b - REAL и op - -, +, *, / , то значение
a op b - REAL , но если op - div или mod то ошибка;
3) если в a op b - a, b - один REAL, а другой INTEGER , то значение типа INTEGER преобразуется к значению типа REAL и результат
операции типа REAL .
Вычисленное значение выражения имеет тот же тип, который определен последней выполненной операцией. Очевидно, что если в
выражении имеется хотя бы одно данное вещественного типа , то весь результат - вещественного типа. Пример : 5.0/3*3 = 5.0.
Данные одной операции выражения называют операндами . Операндами арифметического выражения могут быть : константы,
переменные, выражения, функции. В математике функция - это некоторая последовательность действий, которая по значению
аргументов определяет значение функции. В программирование функции будет соответствовать последовательность операторов,
вычисляющих значение функции, и это значение является значением имени функции. Обращение к функции называют вызовом
функции. Синтаксис формы обращения:
имя_функции (выражение1, выражение2, ... , выражение к) указатель функции.
В программе можно использовать функции двух классов: предопределенные и определяемые программистом.
Нас сейчас интересуют первые.
Sin(3.14) - указатель

 выражение
имя функции
Имени функции присваивается 0 и sin, как данное, имеет значение 0. Поэтому форма выражения
x+ sin(3/14) - допустима.
  данное
данное
В арифметических выражениях допускаются только арифметические функции. В арифметических функциях можно использовать
также выражения, а последние сами могут содержать функции, поэтому получается определенная вложенность функций. Например:
LN(ABS(X)).
Если выражение содержит функции, то первоначально вычисляются они, причем в порядке, позволяющим вычислить внешнюю
функцию, только затем выполняются остальные арифметические операции согласно уже рассмотренным приоритетам.
Переменные.
В программе константа однозначно определяет : имя, тип, значение в разделе
const
по виду значения
pi = 3.1416;
 -real
n = 10;
 - integer
Все три характеристики не измены. Тогда как переменная однозначно определяет только имя. Ни тип , ни значение нельзя узнать по
виду имени переменной. Поэтому требуется все переменные описать в разделе VAR :
VAR
список1_переменных: тип1;
список2_переменных: тип2;
...
списокК_переменных: типК;
Пример:
VAR XR,YR : REAL;
IX,IY : INTEGER;
Возможность изменять свое значение во время работы программы и отличает переменную от константы. Тип и имя переменной
постоянны. Нельзя использовать переменную в операторах действия, если ее значение не определено. Оно не определено до того
момента, пока некоторым способом не передать значение переменной.
Первый способ - чтение данного из файла в оперативную память:
READLN (имя_файла, список_переменных).
Например :
READLN(input, IX,YR)
- из файла INPUT
читается первое значение, оно должно быть числом целого типа и передается
переменной I X как значение, (значение назначается переменой IX ) , затем читается второе данное и передается переменной YR.
Второй способ - оператор присваивания.
Имя_переменной := выражение;
Если переменная слева имеет тип INTEGER , то тип выражения также INTEGER. Если переменная слева имеет тип REAL , то тип
значения выражения INTEGER или REAL.
Примеры:
IX:=3.5 - ошибка,
XR:=IX+5 - норма.
Вывод данных на экран дисплея WRITELN( имя_файла, список_выражений). Константа и переменная - частный случай выражений.
Например: WRITELN( output, ‘XR = ‘, XR, ‘IX =’, IX).
Здесь впервые применены конструкции - ‘текст’. Это новый тип констант - строковый. Они выводятся в файл без ограничений. Readln
и writeln не операторы языка, а предопределенные процедуры.
Теперь мы готовы написать первую программу.
Program Proba1;
Var
ix,iy: Integer;
{Объявление переменных}
Rx, Ry : Real;
Begin
Writeln (output, ‘Введите значения для ix ’ );
Readln(input, ix);
ix:=ix +5;
Writeln( output, ‘Ix=’, ix, ‘Ix+5 =’, ix+5, ‘Iy=’, iy);
{Выражения используются только при вводе}
Writeln(output, ‘Введите значения rx’);
Readln (input, rx);
ry:=ry/3.0;
Writeln( output, ‘Ry=’, ry, ‘Rx=’, rx);
End.
Обмен информацией и файлы.
В случае обмена данными между программой и периферийными устройствами (устройствами ввода-вывода) одним концом канала
обмена данными всегда является оперативная память. Другой конец этого канала в Турбо Паскале определен как файл. Понятие файла
достаточно широко.
Определение :
Поименованная совокупность данных называется файлом.
Файл может быть источником информации - тогда мы читаем из файла
( ввод данных из файла) или приемником - в этом
случае мы записываем в файл ( вывод данных в файл) информацию. Файловая система, реализуемая в Турбо Паскале (диалекте
Паскаля), состоит как бы из двух уровней : логических файлов и физических файлов. Файловый тип в Паскале - это единственный тип
значений, связывающий программу с внешними устройствами ЭВМ.
Логический файл.
Логический файл описывается как переменная одного из файловых типов, определенных в Паскале. После того как в программе в
разделе описания переменных объявлена файловая переменная, она может быть использована как средство общения с любым
физическим файлом, не зависимо от природы последнего. Само имя физического файла может появиться в программе только один
раз, когда специальной процедурой устанавливается , что объявленный логический файл будет служить средством доступа именно к
этому физическому файлу. После этого все обращения к файлу будут производиться через файловую переменную. Введение
логического файла позволяет программисту не задумываться о технических проблемах организации обмена данными. В Паскале
несколько файловых типов, мы рассмотрим пока - один текстовый : text. Текстовые файлы - это файлы, состоящие из кодов (ASCII),
включая расширенные и управляющие коды. Текстовые файлы организуются по строкам и обязательно содержат специальный код,
называемый концом строки. Любую информацию ( числовую, символьную или строчную) текстовый файл хранит в виде символов, ее
изображающих. При наборе букв и цифр на клавиатуре, создается текстовый файл. В Турбо Паскале определены две текстовые
файловые переменные: input и output.
Input связан с вводом с клавиатуры, output - с выводом на терминал. Например:
VAR
input, output : text;
В Турбо Паскале указание файлов input и output можно опускать, они используются по умолчанию.
Конструкторы.
В языках программирования есть конструкторы, которые позволяют из линейных последовательностей утверждений группировать
составные утверждения. Семантика конструкторов типовая : в зависимости от условия составному утверждению передается
управление или нет при выполнении программы. Первым простым конструктором является конструктор ветвлений полный:
Если условие
то серия1
иначе серия2
Все
или не полный
Если условие
то серия
Все
Подчеркнем , что это не оператор языка программирования в том смысле, что он непосредственно оперирует с данными, это оператор
над утверждениями. В любом языке программирования присутствует конструктор ветвлений. Его семантика не зависит от синтаксиса,
так в Паскале - это:
IF условие
THEN оператор
ELSE оператор - для полного конструктора ветвлений и
IF условие
THEN оператор - для неполного конструктора ветвлений.
Если ветви THEN и ELSE содержат более одного указания, то указания заключаются в скобки BEGIN и END. В любой из ветвей THEN
или ELSE можно применять конструкторы, т. е. допустимы вложения :
IF ... THEN
...
ELSE
...
IF ... THEN ... ELSE
IF ... THEN ... ELSE .
Если после ELSE нужно расположить группу операторов , выполняемых тогда и только тогда, когда условие ложно, то ее заключают в
операторные скобки
BEGIN и END.
Отсутствие скобок не считается синтаксической ошибкой, это - семантическая ошибка.
Например:
IF a = 3
IF a = 3
THEN
THEN
BEGIN
BEGIN
a := 6;
a :=6;
b := 24;
b :=24;
END
END
ELSE
ELSE
a := 9;
BEGIN
b :=12;
a := 9;
b :=12;
END
Пусть а =3 , то а станет равна 6,
При а = 3 , то новое а станет
b станет равно 24 и затем новое
равное 6, b равно 24.
b станет равно 12.
При а не равном 3, новое а
При а не равном 3 , новое а
станет равно 9 и b равно 12.
станет равно 9, а b равно 12.
Этого вы хотели ?
Данные типа BOOLEAN.
Тип BOOLEAN состоит из двух элементов с именами FALSE и TRUE. Над данными этого множества определен порядок :
true.
Объявление переменных типа BOOLEAN в программе var
zlog, xlog, ylog : boolean.
false <
Операции над элементами BOOLEAN.
1)
NOT - это одинарная ( унарная) операция отрицания ( нет, не) :
not false = true
not true = false .
2) OR - это бинарная операция логическое сложение ( союз или):
false or false = false
false or true = true
true or false = true
true or true = true.
3)
AND - это бинарная операция логическое умножение ( союз и ):
false and false = false
false and true = false
true and false = false
true and true = true.
Для данных множества BOOLEAN определены операции: = - равно,
равно,
>= - больше или равно.
Пусть xlog := false;
ylog:= true;
тогда zlog:= xlog < ylog; - true, zlog:= xlog > ylog;.
< - меньше, > - больше, <> - не равно , <= - меньше или
Логические выражения.
Определение:
Логическое выражение - корректная запись, содержащая логические данные, скобки и логические операции.
Например: при xlog = true, ylog = false, zlog = false;
not (( xlog or ylog) and ( zlog and ylog)) - true;
not xlog or ylog and zlog and ylog - false;
(not xlog) or (( ylog and zlog) and ylog) - false.
Отношение
Над данными типа REAL, INTEGER определены операции =, <>,>, <, >=, <= со значением BOOLEAN. Если X= 4 то xlog:= x<3 - false.
Если X = 0
то xlog = x< 3 - true.
Логическое выражение часто используется в условном операторе или самостоятельно. Условие - это отношение или логическое
выражение. Рассмотрим приоритет операций, если
в логическом выражении есть арифметические выражения:
первый - not, +, - ;
второй - *, /, div, mod, and;
третий - +, -, or;
четвертый - =, <>, < , >,<=, >=.
Например :
x - real, y -integer, xlog, ylog, zlog - boolean;
zlog:=(((x-y)<(x+5)) and not xlog) при x = 3, y = 4, xlog, ylog = false;
1) x-1 = -1 , 2) x+5 = 2 , 3) not xlog = true , 4) -1<2 , 5) true and true = true
zlog = true.
Данные типа CHAR.
Множество CHAR - это упорядоченное множество литер. В стандарте это множество не определено окончательно, т. е. его
определение связано с реализацией. Потому можно ли ввести символ с клавиатуры и отобразить на экране, различают видимые и
невидимые символы. В Паскале возможна работа только с видимыми символами типа char.
К видимым символам относятся :
1)
2)
3)
4)
латинские строчные и прописные буквы;
русские строчные и прописные буквы;
знаки препинания , разделители;
знаки операций, скобки и т. д.
В любой реализации множество значений рассматриваемого типа должно обладать следующими свойствами:
- это множество содержит цифры от 0 до 9, которые считаются упорядоченными по возрастанию изображаемых ими чисел;
- если строчные латинские буквы от a до z допускаются реализацией, то они должны быть упорядоченными по алфавиту, тоже самое
относится к прописным латинским буквам.
Использование данных типа CHAR в программе :
а) константы - ‘ A’ - один символ;
б) переменные - VAR x ,y :char.
Операции над элементами CHAR.
В Паскале нет операций над значениями типа char, которые давали бы значение этого же типа. Переменную типа char можно
присвоить -x:= ‘A’; y:= x;.
Над множеством char определены следующие функции:
CHR ( i ) - дает литеру ( значение типа char) с порядковым номером - i ; ORD ( c ) - дает порядковый номер литеры с во множестве
значений типа char. Эти функции являются обратными по отношению друг к другу chr ( ord( c ) ) =c и ord ( chr(i) ) = i.
Определение следующего символа - это SUCC ( символ i ) => символ i+1 :
x:=succ (‘A’) => x:=’B’;
Определение предыдущего символа - это PRED( символ i ) => символ i-1 :
x:= pred( ‘B’ ) ; => x:= A;. Для самого последнего ( первого) символа функции не определены. Было уже отмечено, что данные типа char
линейно упорядочены.
По определению множество Y линейно упорядочено , если для любых x1 и x2 можно сказать , что x1< x2, x1>x2 или x1 =x2.
Отношения “<”, “>”, “=” известны для числовых множеств, то возникает вопрос, что означают меньше, больше, равно для элементов
не числового множества, каким является множество CHAR. На множестве CHAR эти отношения - отношения лексикографического
порядка, т. е. литера x1, следует в алфавите раньше литеры x2 по порядку, или что еще проще номер литеры x1 меньше номера
литеры x2. Поэтому на множестве CHAR:
x1 < x2  ord(x1) < ord(x2);
x1> x2  ord(x1) > ord(x2);
x1 = x2  ord(x1) = ord(x2) и т. д..
Отношения =, <, >, <=, >=, <> - новые операции на множестве CHAR со значением во множестве BOOLEAN.
Типы данных в языке Паскаль.
Мы изучили типы данных REAL, INTEGER, BOOLEAN и CHAR. Эти типы определены в языке Паскаль так, что о типе известно его имя и
все его элементы. В этом и состоит собственно определение типа. Поскольку определение типа дано в языке, а не в программе на
этом языке, то такие типы называют предопределенными, т. е. известными до написания программы. Типы данных, определяемые в
программе на языке Паскаль, называют определяемые программистом. К предопределенным типам также относят тип text . Других
предопределенных типов в стандарте языка Паскаль нет. Многие диалекты допускают расширение класса предопределенных типов.
Предопределенные типы, для того чтобы иметь возможность ссылаться на них, имеют зарезервированные имена. Типы,
определяемые программистом , по тому указано ли для них имя или нет , делятся на два подкласса: анонимный и именованный. Все
типы , вводимые программистом в программу указываются в разделе TYPE, который предшествует разделу VAR, тем самым
обеспечивается правило: имя не может быть использовано прежде, чем оно определено.
Синтаксис :
Type
имя_типа1=тип1;
имя_типа2=тип2;
...
имя_типаК=типК;
Здесь имя_типа1 - идентификатор , интерпретируемый как имя типа, записывается по тем же правилам, что и имя переменной. Тип1 это или имя типа или анонимный тип, т. е. в этом месте указываются некоторым способом все элементы анонимного типа. Таким
образом, оператор TYPE именует анонимный тип или вводит новое имя для некоторого именованного типа.
Определение:
Тип данных называется скалярным, если одно значение соответствует одному элементу типа, иначе составным.
Согласно этому критерию предопределенные типы REAL, INTEGER, BOOLEAN, CHAR - скалярные, а тип TEXT - составной. Тип TEXT - это
множество файлов, каждый из которых может быть значением файловой переменной. В свою очередь файл состоит из нескольких
значений типа REAL, CHAR, INTEGER. Типы, определяемые программистом, тоже могут быть скалярными или составными.
Можно ввести новую классификацию типов, но уже на множестве скалярных типов. В качестве критерия классификации выберем
определена ли для типа функция ORD(). Эта функция отображает все элементы типа на целочисленный интервал.
Для CHAR ORD(X) определена со значениями на интервале [0,255]. При этом, если ХCHAR ,то ORD(X) - десятичный код или
порядковый номер Х во множестве CHAR.
Для множества INTEGER также определена функция ORD(X).
ORD() : INTEGER -> [MININT,MAXINT]. ORD(X)=Х, если
X INTEGER.
Для множества BOOLEAN ORD(X) : BOOLEAN->[0,1],
false->0,true->1.
Для всех скалярных , определяемых программистом типов, функция ORD() определена.
Таким
образом,
если
для
скалярного
типа
определена
функция
ORD() , то он называется ординальным (порядковым), иначе не ординальным. Для всех ординальных типов определены функции
SUCC(), PRED() за исключением “минимального” элемента, т. е. самого левого по порядку и “максимального” - правого по порядку,
если расположить элементы в порядке возрастания значения функции ORD(). Для всех ординальных типов определены операции
отношения R: =,<>, < , >, <=, >= . Если X и Y принадлежат одному и тому же ординальному типу то X R Y ORD(X) R ORD(Y).
ПЕРЕЧИСЛИМЫЙ ТИП
Перечислимый тип - это множество значений, заданных перечислением своих элементов. Каждый элемент с синтаксической точки
зрения - это идентификатор. С семантической - значение, с прагматической - обозначение в программе некоторого моделируемого
объекта.
Анонимно перечислимый тип задается :
(идентификатор1, идентификатор2, ... , идентификаторК).
Все имена должны быть уникальны и не могут быть использованы иначе, как имя перечислимого типа.
Пример : (А01, А02,А03,А04,А05) - множество состоит из пяти элементов.
ORD(идентификаторi) = i-1, i= 1, ..., k.
SUCC(идентификаторi) = идентификаторi+1, i=1, ... , k-1.
PRED(идентификаторi) = идентификаторi-1, i=2, ... , k.
Идентификаторs R идентификаторr  s R r, s,r = 1, ... , k.
Других операций нет. Зачем в Паскале введен этот класс типов ?
Сделано для того, чтобы текст программы на Паскале представлял собой решение задачи в терминах ( на английском языке),
естественных для данной задачи. В этом случае наш пример неудачен. В тех случаях , когда имена типа естественно обозначают
объекты, то и программа становится более понятной.
Пример:
ColorOfHour =(blond, darkhaired, brounhaired);
sex =(male, female);
height= ( very tall, tall, medium, short, very short);
Данные перечислимого типа нельзя вводить и выводить, т.е. операции чтения и записи для таких данных не определены. Что такое
RED на внешнем устройстве - это строка символов, но не данное типа COLOR. Поэтому невозможно определить операцию чтения и
записи на внешнее устройство для данных перечислимого типа. Каждый новый перечислимый тип - это новое множество, которое не
пересекается ни с одним из ранее введенных, не предопределенными типами, т.е. перечислимые типы состоят из элементов , у
которых хотя бы один не может быть элементом другого типа.
TYPE COLOR= (RED, BLUE, YELLOW, BRAWN, GREEN);
COL1 = (RED, BLUE, BLACK)
не допустимо ,т. к. их пересечение не пусто.
Аналогично CHAR  INTEGER = 0, CHAR  BOOLEAN = 0
и INTEGER  BOOLEAN = 0.
Любой предопределенный скалярный тип не пересекается с любым перечислимым. Можно BOOL = (TR,FL) , но нельзя BOOL = (
TRUE, FALSE).
По признаку уникальности элементов множества все перечислимые типы и предопределенные CHAR, INTEGER, BOOLEAN называют
базовыми типами, эти множества нельзя построить из элементов других множеств, только из собственных.
Рассмотрим метод введения в программу нового типа, но обязательно как подмножество базового и только базового множества. При
этом элементы вводимого множества расположены в базовом без пропусков, непрерывно или подряд, начиная с некоторого X и
заканчивая некоторым Y. Очевидно что X<=Y. Такое новое множество называют диапазоном.
Анонимно оно определяется : X .. Y, где X,Y принадлежат базовому типу и являются константами.
Примеры:
1 .. 100
‘a’ .. ‘i’
RED .. YELLOW.
По имени - оно определяется с помощью нового имени для диапазона.
Примеры:
COL = RED .. YELLOW;
LIT1 = ‘a’ .. ‘i’;
LIT2 = ‘j’ .. ‘s’;
- по имени именуется анонимный тип.
LIT3 = ‘r’ .. ‘z’;
LIT3LIT3 = LIT3; - по имени именуется тип с именем.
Для базового типа мощностью N>=1 можно ввести (N+1)*N/2 различных диапазонов.
Рассмотрим следующие определения типов:
TYPE
T1 = (R, Q, P, T);
T2 = 0 ..3;
T3 = T1;
VAR
X: T1;
Y: ( R, Q, P, T);
Z: T2;
A: 0 .. 3;
B: 0 ..3;
C: T3;
Типы T1 иT3 идентичные, тип T1 и тип переменной Y недопустимы, тип T2 и типы переменных A и B совместимы. Два скалярных
типа идентичны, если они объявлены TYPE X= Y. Два скалярных типа совместимы, если они идентичны, если один тип - диапазон
другого, если оба типа - диапазоны одного типа. Переменные совместимы по присваиванию:
var1:= exp; - если тип переменной и тип выражения - один и тот же тип ;
- если тип переменной и тип выражения совместимы и
значение exp принадлежит типу var1;
- если тип переменной REAL , а тип exp - INTEGER.
Операции определенные для базового типа определены и для диапазона, если они не приводят к конфликту совместимости по
присваиванию.
Диапазоны используются в программах, например, там, где необходимо системными средствами обеспечить контроль значения на
принадлежность диапазону.
Например:
var
digit: integer;
...
readln(input, digit);
if (-4<=digit) and (digit <=5) then ...
else writeln(output, ‘ число вне
диапазона - 4,5’);
...
а можно так
type
diap45 = -4 .. 5;
var
digit: diap45;
...
readln( input, digit);
...
- тогда системное сообщение и аварийное завершение . Диапазоны используются чаще всего на базе INTEGER, реже на базе CHAR и
иногда на базе перечислимого типа.
КОНСТРУКТОРЫ ЦИКЛОВ.
Конструктор цикла for
Конструктор цикла for - двух видов с автоувеличением и автоуменьшением. Рассмотрим сначала цикл с автоувеличением:
FOR <переменная цикла> := <выражение1> to <выражение2> do
оператор;
Тип переменной цикла и выражений должен быть ординальный. Переменная цикла должна быть объявлена в том же блоке, где
появляется и сам оператор for. Тело цикла - оператор не должен изменять значение переменной цикла как в операторах
присваивания слева так и в процедурах read, readln. Если оператор - тело цикла - содержит другой цикл, то в последнем запрещено
использовать переменную цикла внешнего как переменную цикла внутреннего. Значения выражения1 и выражения2 вычисляются
только один раз - перед входом в цикл.
Семантика оператора:
1.
2.
3.
Вычисляются выражение1 и выражение2;
Если значение выражения1>выражения2, то тело цикла не выполняется, считается , что цикл работу закончил;
Иначе, если значение выражения1<=значения выражения2, то
3.1 переменная_цикла:= выражение1;
3.2 выполняется тело цикла;
3.3 переменная_цикла:=SUCC(переменная-цикла);
3.4 Если переменная_цикла не равна выражению2 то повторение 3.2,3.3,3.4;
Иначе выполнить тело цикла и цикл
завершает свою работу.
4.
Значение переменной по выходу из цикла не определено.
Пример:
VAR i:char; j: integer;
for i:=’a’ to ‘f’ do
for j:= 1 to 8 do
writeln(‘это клетка’, i:1, j:1);
Будут распечатаны в столбик все названия шахматных клеток , при этом под букву и номер будет отведено по одной позиции.
Пример:
var
icol: incol;
...
readln(input, indigitcol1, indigitcol2);
for icol:= red to yellow do
begin
if ord(icol)= indigit1+1 then incol1:= icol;
if ord(icol)=indigitcol2+1 then
incol2:=icol;
end
Перейдем к рассмотрению конструктора for с автоуменьшением:
FOR переменная_цикла:= выражение1 DOWNTO выражение2 DO
оператор
Ограничения те же, что и в цикле с автоувеличением.
Семантика оператора:
1.
2.
3.
Вычисляются выражение1 и выражение2;
Если значение выражения1<выражения2 . то тело цикла не выполняется;
Иначе, если значение выражения1 >= значения выражения2 , то
3.1. Переменной_цикла:= выражение1;
3.2. Выполняется тело цикла - оператор;
3.3. Переменная_цикла:=PRED(переменная_цикла);
3.4. Если переменная_цикла не равна выражению2, то повторение 3.2,3.3,3.4
Иначе выполнить тело цикла и цикл заканчивает
свою работу.
Где применяются конструкторы цикла for? В тех задачах, где число повторений выполнения тела цикла заранее известно. Такие циклы
называются арифметическими.
Пример:
Вычислить и напечатать первые 100 чисел Фибоначчи - 0,1,1,2,3,5, ... , где
F0=0, F1=1, FN+2=FN+1 + FN - числа Фибоначчи.
Program exampl(input, output);
var
i: integer;
FN,FN1,FN2;
N: integer;
begin
readln(input, output);
if (N>=0) and (N<=100) then
begin
FN:=0; FN1:=1;
for i:=2 to n do
begin
FN2:=FN1 + FN;
writeln(output, ‘числа’, I:2,’число’,FN2:4);
end;
end
else
writeln(‘неправильно указано количество’);
end.
Конструктор цикла while
Во многих задачах количество повторений действий может быть неизвестным, оно может изменяться от 0 до некоторого наперед
неизвестного числа. Такие циклы называются итерационными.
Пример:
Найти сумму гармонического ряда 1+1\2 +1\3 +1\4 + ... +1\N + ... для всех членов ряда больше Е, где 0<Е<1.
Как решаются такие задачи?
SN:= 1\к , SN+1= SN +1\(N+1);
SN+1 -SN= 1\(N+1) и 1\(N+1) <=Е , то не включать, а т. к. 1\N -> 0 при n ->  , то 1\(N+2) , 1\(N+3) ...<E.
Программа:
program exammple5(input, output);
VAR eps, s : real;
n: integer;
begin
writeln(output, ‘ Введите значение ЕPS’);
readln( input, EPS);
if (eps>0.0) and (eps<1.0) then
begin
s:=0.0; n:=1;
while( 1\(n+1)>eps) do
begin
s:=s+1\n; n:=n+1;
end;
writeln(output,’Сумма ряда =’, s);
end
else
writeln(output, ‘ Некорректное значение eps’);
end.
Синтаксис конструктора:
while логическое условие do
оператор
Семантика конструктора:
1.
2.
вычисляется логическое условие;
Если его значение истина, то выполнить оператор ( тело цикла) и повторить пункты 1 и 2 до тех пор пока значение логического
условия не станет ложным;
3.
Иначе цикл работу завершает.
ВНИМАНИЕ!
1.
Тело цикла выполняется после проверки логического условия, поэтому если на первом шаге значение условия будет ложным, то
тело цикла не выполняется, а цикл завершает свою работу.
2.
Состояние, когда при каждой итерации логическое условие остается истинным называется зацикливанием, чтобы избежать такой
ситуации необходимо, но не достаточно , чтобы операторы тела цикла изменяли переменные, входящие в логическое условие.
Пример:
1.
Не изменяется условие - вечный цикл.
While true do
s:=s+1\n;
2.
Тело цикла не выполнится ни разу, если eps<0.5.
s:=0; n:=1;
while (1\(n+1)<eps) do
s:=s+1\n;
3.
Зацикливание.
s:=0; n:=1;
while (n+1)>eps do
s:=s+1\n;
Конструктор цикла repeat
Синтаксис конструктора.
Repeat
операторы
until логическое условие
{; перед until не ставится}.
Семантика конструктора.
1.
2.
3.
Выполняются операторы;
Вычисляется логическое условие;
Если условие ложно, то выполняются пункты 1 и 2, иначе цикл завершает свою работу.
При выполнении этого оператора цикла последовательность операторов, находящаяся между repeat и until, выполняется один и более
раз.
ЗАДАЧА.
С терминала вводится несколько чисел xi до тех пор пока не будет введено 0.0. Каждое число возводится в i степень и суммируются.
Из полученной суммы последовательно извлекается квадратный корень до тех пор пока 1<=S <=2. Напечатать количество извлечений.
Program exampl6( input, output);
VAR
s,a,b,x : real;
r,i: integer;
begin
s:=0; n:=1; k:=0;
writeln(output, ‘ Введите действительные числа,
последнее должно быть 0’);
readln(input, x);
if abs(x) > 1/0E-7 then
begin
repeat
a:=1;
for i:=1 to n do
a:=a*x;
s:=s+a; n:=n+1;
readln(input, x)
until abs(x)<1.0E-7;
b:=abs(s);
while(b>2.0) do
begin
b:=sqrt(b); k:=k+1;
end;
end;
writeln(output,’ Число извлечений корня’,k);
end.
В этом примере были использованы все три вида циклов вложенных друг в друга.
КОНСТРУКТОР ВАРИАНТОВ.
Характерной чертой многих алгоритмов является широкая разветвляемость задаваемых ими вычислительных процессов . С одним
из средств Паскаля для задания разветвлений мы уже знакомы - это условный оператор, предписывающий в зависимости от условия
выбрать тот или иной оператор .
На практике довольно часто встречаются случаи, когда вычислительный процесс надо разветвить не по двум, а по k (k>2) возможным
путям. Это можно сделать и с помощью условного оператора , учитывая рекурсивность его определения.
If A1 then S1
else if A2 then S2
else if A3 then S2
...
if An then Sn
else <пустой оператор>
Однако в этом случае запись условного оператора может оказаться весьма громоздкой и не наглядной. Вложенность, если она
больше семи, усложняет восприятие логики программы. Линейная последовательность предпочтительнее, чем вложенная , в тех
случаях когда уровень вложения глубок. Для таких случаев в Паскале существует конструктор CASE.
Синтаксис конструктора.
Case < селекторное- выражение>of
<список-констант1>: <оператор1>;
<список-констант2>: <оператор2>;
...
<список-констант k>: <оператор k>
end
Селекторное-выражение используется для выбора (селекции) оператора. Выражение определено над данными ординальных типов
совместимых по присваиванию. Список констант - одна или несколько констант перечисленных через запятую и того же типа , что и
тип селектора.
Семантика конструктора.
1.
2.
3.
4.
Вычисляется селектор-выражение;
Каждая альтернатива (ветвь) просматривается сверху вниз;
В пределах одного варианта просматривается список констант;
Если в этом списке есть константа равная селекторному выражению , то управление передается оператору этого варианта, после
его выполнения управление передается оператору следующему за end;
5. Если нет константы равной селекторному выражению, то фиксируется ошибка в программе.
В Турбо- Паскале допускается не обязательная ветвь ELSE . Если значение селекторного выражения не совпало ни с одной константой,
то управление передается оператору варианта ELSE.
Пример 1:
var
litera: char;
...
case litera of
‘a’ ,’A’: writeln(output, ‘это буква А’);
‘b’,’B’: writeln(output, ‘это буква B’);
...
end;
Пример 2:
Перевод цифр шестнадцатеричной системы счисления в десятичную систему счисления.
Var
chislo : integer;
litera : char;
...
case litera of
‘0’: chislo:= 0;
‘1’; chislo:= 1;
‘2’: chislo:= 2;
‘3’: chislo:= 3;
‘4’: chislo:= 4;
‘5’: chislo:= 5;
‘6’: chislo:= 6;
‘7’: chislo:= 7;
‘8’: chislo:= 8;
‘9’: chislo:= 9;
‘A’: chislo:= 10;
‘B’: chislo:= 11;
‘C’: chislo:= 12;
‘D’: chislo:= 13;
‘E’: chislo:=14;
‘F’: chislo:= 15
end;
или
case litera of
‘0’,’1’,’2’,’3’,’4’,’5’,’6’,’7’,’8’,’9’: chislo:= ord(litera) - ord(‘0’);
‘A’,’B’,’C’,’D’,’E’,’F’: chislo:= 10 + ord(litera) - ord(‘A’)
end;
Пример 3 :
С терминала вводится два числа из диапазона 1 ..3. Число 1 обозначает красный цвет, число 2 - голубой , число 3 - желтый . Вывести на
экран название цвета , полученного при смешивании двух исходных цветов.
Program examplecolor (input, output);
Type
incoldig = 1..3;
{код цвета}
color = (red, blue, yellow, purple, brown, green);
{множество цветов}
incol = red .. yellow;
{подмножество исходных цветов}
Var
indigitcol1, indigitcol2: incoldig;
incol1, incol2 : incol;
rescol: color;
begin
{ввести и расшифровать цвет кода}
writeln(output, ‘Введите код первого цвета из диапазона 1..3’);
readln( input, indigitcol1);
writeln( output, ‘ Введите код второго цвета из диапазона 1..3’);
readln(input, indigitcol2);
{расшифровка цвета}
if indigitcol1 = 1 then incol1:= red;
if indigitcol1 = 2 then incol1:= blue;
if indigitcjl1 = 3 then incol1:= yellow;
if indigitcol2 =1 then incol2:= red
else if indigircol2 =2 then incol2:= blue
else if indigitcol2 =3 then incol2 := yellow;
if (incol1 = red) and (incol2 = red) then rescol:=red;
if (incol1 = red) and (incol2 = blue) or (incol1 = blue) and (incol2 =red) then
rescol:= purple;
if (incol1 = red) and (incol2 = yellow) or (incol1 = yellow) and (incol2 =red) then
rescol:=brown;
if (incol1 = blue) and (incol2 = blue) then rescol:= blue;
if (incol1 = blue) and (incol2 =yellow) or (incol1 = yellow) and (incol2 = blue) then
rescol:= green;
if (incol1 = yellow) and (incol2 = yellow) then rescol:= yellow;
, но
case indigitcol1 of
1: incol1:= red;
2: incol1:= blue;
3: incol1:= yellow
end;
case indigitcol2 of
1: incol2:= red;
2: incol2:= blue;
3: incol2:= yellow
end;
case incol1 of
red: case incol2 of
red: rescol:= red;
blue: rescol:= purple;
yellow: rescol:= brown
end;
blue: case incol2 of
red: rescol:= purple;
blue: rescol:= blue;
yellow: rescol:= green
end;
yellow: case incol2 of
red: rescol:= brown;
blue: rescol:= green;
yellow: rescol:= yellow
end
end;
case rescol of
red: writeln(output,’красный’);
blue: writeln(output,’голубой’);
yellow: writeln(output,’желтый’);
purple: writeln(output,’фиолетовый’);
brown: writeln(output,’коричневый’);
green: writeln(output,’зеленый’)
end;
end.
Программы читаются значительно проще, если уровни вложенности небольшие, более глубокий уровень сложности требует
напряжения памяти, что неизбежно приводит к ошибкам.
Данные типа
ARRAY ... OF ...
Рассмотрим вектор точек: (x1,y1), (x2,y2), .... , (xn,yn) , когда точка - представлена двумя действительными координатами .
Поэтому мы можем рассмотреть два вектора: <x1 ,x2 , ... , xn> и <y1,y2, ... , yn> для которых установлено неявное соответствие по
номеру элемента вектора. В каждом векторе его компонента - скалярная величина. Каждая компонента принадлежит одному и тому
же типу. Совокупность таких компонент представляют одно понятие - вектор. В языках программирования такие структуры данных
называются массивами - это совокупность однородных (однотипных) элементов.
Массив не скалярное данное, это составное данное имеющее определенную организацию - векторную и характеризуется
тем, что каждое данное компоненты вектора имеет один и тот же тип. Имеет смысл рассматривать набор массивов с одинаковой
организацией и одним и тем же типом для компонент как тип массивов, т. е. как множество массивов.
Над массивами определены две операции:
- присваивания, когда значения одного массива можно передать другому (реально присваиваются элементы по
компонентам);
- выборка компонента из множества - нужно указать имя массива и порядковый номер компоненты, значением операции
является выбранная компонента массива.
Синтакис_описания_типа_для_массивов.
TYPE
<имя> = ARRAY [ <тип_индекса> ] OF <тип_компоненты>
Семантика_описания_массива.
Имя - это имя типа для массива, который вводится в разделе type. Тип_индекса - это множество значений, которые может
принимать индекс (порядковый номер) компоненты массива. Тип_компоненты - это тип данных, которому принадлежат значения
компонент массива .Синтаксически тип индекса может быть задан по имени , а может быть анонимным.
Ограничения:
Тип_индекса - это только любой ординальный тип. Тип_компоненты - любой предопределенный или ранее введенный
программистом.
Примеры:
1.
TYPE
RAS = 1 ..100;
TARR = array [RAS] of real;
VAR
x: TARR; y: TARR;
Оператором TYPE введены имена для типа индекса и имена для типа массивов. Оператором VAR определены массивы X и
Y. Каждый из которых состоит из 100 элементов типа real. Можно и так
2.
TYPE
TARR = array [1 .. 100] of real;
VAR
x, y: TARR;
или так
3.
VAR
x, y: array [1 .. 100] of real;
Последнее определение менее предпочтительно, чем второй способ, из-за того, что придется вводить понятия близкие по
смыслу к “совместимости по присваиванию”. Чтобы этого не делать, лучше определить тип для массивов, а потом использовать его для
определения самих массивов.
Массив, тип компоненты, которых является скалярным, называется одномерным или вектором.
Примеры:
TYPE
prim1 = array [-35 .. -10] of char;
prim2 = array [‘a’ .. ‘i’] of char;
hrim3 = array [color] of color;
...
Последний массивный тип содержит 6 компонент, индекс_компоненты - некоторый цвет, значения самих компонент тоже
цвет.
Синтаксис_операций_над_массивами:
Операция присваивания:
X:=Y; - если оба массива одного типа ( но не для всех реализаций);
Операция селекция <имя_массива> [< индексное_выражение>] , где индексное_выражение - любое выражение совместимое по
присваиванию с переменной типа индекса для этого массива. После селекции компонента может использоваться во всех операциях
как скалярная величина соответствующего типа.
Пример:
Var
c: prim3;
...
c[red]:= red;
...
c[blue]:= blue;
...
Таким образом, мы познакомились с одномерным массивом - вектором. В языке Паскаль существуют массивы с более
сложной организацией: двухмерные -вектор векторов, трехмерные - вектор двухмерных массивов и т. д. Количество измерений
ограничено реализацией.
Синтаксис_описания_многомерных_массивов_:
TYPE
<имя_типа> = array [<тип_индекса1>] of array [< тип_индекса2>] of
<тип>;
VAR
x: <имя_типа>;
Семантика_:
Аналогично одномерному массиву, но описывается структура двухмерная, моделью ее является матрица.
Известно также, что
array [<тип_индекса1>] of array [<тип_индекса2>] of <тип> 
array [<тип_индекса1>,<тип_индекса2>] of <тип> .
Операция селекции записывается как
TYPE
cor = array[1 .. 100] of array [1 .. 100] of real;
VAR
c: cor;
...
write(c[1,2]); {Печать второго элемента в первой строке}
Описание трехмерных массивов и массивов с большим числом измерений идентично двухмерному с учетом количества
индексов. В оперативной памяти все компоненты массива размещены последовательно, без пропуска участков памяти между
компонентами.
Упаковка и распаковка массива
Данные, являющиеся значениями скалярных типов, занимают мало места в памяти ЭВМ. Одна литера - 1 байт ( 8 двоичных
разрядов). Для чисел различных типов в зависимости от реализации отводят несколько байтов. Для данных производного типа
требуются значительные объемы памяти. Так в ячейку, состоящую из нескольких байтов, можно поместить столько же литер.
Для этого нужно указать служебное слово packed. Значение производных типов могут быть представлены в памяти ЭВМ в
упакованном или неупакованном виде . Упакованное представление требует меньшего объема памяти, но больше времени для
обработка.
Синтаксис описания- packed array [<тип_индекса>] of <тип_переменной>;
Доступ к отдельным компонентам упакованных массивов часто обходится довольно дорого и зависит от ситуаций и особенностей
конкретной реализации. Поэтому иногда следует упаковывать и распаковывать уже упакованные массивы с помощью одной
операции. Это можно сделать с помощью функций Pack и Unpack. Пусть U - неупакованный массив
array [ A .. ] of T, а
P
- упакованный массив packed array [ B .. C] of T, причем
ord(D) - ord(A) >= ord( C ) - ord( B), тогда Pack(U,I,P) - упаковать часть
U начиная с I компоненты в P, а Unpack(P, U, I) - распаковать P в U, начиная с I компоненты.
Строковый тип данных
Данные типа packed array [1 .. N] of char, где N - целое число, называют строками , а тип строковый. N >1, т. е. не существует
пустых и одномерных строк. Значением строкового типа является массив символов образующих некоторую строку, если
проигнорировать, что это массив, Но любая строка символов - это массив символов и только. Но есть исключение, все данные
скалярного типа имеют две формы представления: константную и переменную - все данные любого массивного типа
представлены в именной форме( т. е. через имя переменной) и не имеют константной формы. Исключение сделано только для
строковых типов - это не отдельный тип, а частный случай массивного типа . Строковые константы преимущественно
используются в операторах вывода - ‘ введенное значение есть = ’, редко они используются в числовых программах, чаще при
обработке текстов.
Так как данные строкового типа - это массивы, то над ними допустимы только операции, определенные над массивами :
присваивания и выборки.
Для строковых типов введено понятие совместимости по типу и по присваиванию. Два строковых типа совместимы, если они
определяют множества с одинаковым числом элементов.
Если две строки имеют одинаковую длину, но принадлежат разным типам, то типы совместимы.
Выражение строкового типа V:=E совместимо по присваиванию с переменной строкового типа , если типы выражения E и
переменной V - совместимы.
К сожалению, в стандарте Паскаля нет специально определенных функций для работы над строками:
- определить с какой позиции строки y строка x входит в y;
- определить входит ли строка x в строку y;
- определить содержит ли строка x символы некоторой строки y и т. д.
Для этих целей используется библиотека Турбо Паскаля.
К строкам применимы все шесть операций отношений, но строки при этом должны иметь одинаковую длину.
Пример:
TYPE
stroca1 = packed array [1 .. 100 ] of char;
stroca0 = packed array [1 .. 5] of char;
astr = array [1 .. 20] of stroka0;
VAR
s1, s2 : stroka1;
s3, s4 : stroka0;
s5 : astr;
...
s3:= ‘колос’;
s4 := ‘колокол’;  ошибка
s5[1]:= s3;
s4 := ‘зима’;  ошибка
s1 := s2;
...
Пример:
Написать программу, которая читает строку символов не более 80 и определяет частоту вхождения в строку каждой буквы
латинского алфавита от “a” до “i”.
Замечания:
1)
2)
3)
читать всю строку одним оператором нельзя, читать только поэлементно (посимвольно);
нет массивов переменной длины, поэтому под массив выделим 80 позиций;
будем читать строку до тех пор, пока ее не исчерпаем или не встретим символ с ord() = 255.
Схема:
i:= 0; <установить первую позицию>
repeat i:= i+1;
<читать>(ai);
< увеличить счетчик для ai на 1 >
until (  (i< 80 и ord(ai)  255));
<печать результатов>
Программа:
program num(input, output);
TYPE
arcout = array [char] of integer;
VAR
symb: char;{читаемый и анализируемый элемент }
count: arcount;
str : packed array [1 .. 80] of char ; { строка}
i, j : integer;
BEGIN
{обнулить массив счетчиков }
for symb := chr(0) to chr(255) do
count [symb] := 0;
i:= 0;
repeat
i:= i + 1; read( input, str[i]);
count[str[i]] := cjunt[str[i]] + 1;
until ( not( (i<80) and ( ord(str[i]) <>255 );
writeln( output, ‘в этой строке’);
for j := 1 to i do
write(output, str[j]);
writeln(output, ‘символ встречается’ );
for symb := ‘a’ to ‘i’ do
write(output, ‘’, symb, ‘’, count[symb]:10);
END.
В Турбо Паскале введен тип string вместо описанного выше
packed array ... of char . Тип данных string иногда
называют стринговым.
Пример:
VAR
Name : string [20];
Память , отведенная для хранения значения переменной Name, составляет 21 байт, 1 байт содержит текущую длину строки. В
каждом байте хранится одна литера . Каждая литера представляет собой значение типа char.
В стринговых выражениях используется операция конкатенации (слияния), которая обозначается знаком “+” .
Пример:
Name := Name + ‘пять’;
Name:= ‘строка’;
В зависимости от решаемой задачи можно пользоваться или string, или packed array ... of char (например при формировании
строки, начиная с пустой).
Записные типы
В языке Паскаль можем выделить виды данных , используя критерий сложности строения данных: скалярные и составные.
К скалярным можем отнести : целочисленные, реальные, логические, литерные, предопределенные данные, перечислимые,
диапазонные.
К составным данным пока - неупакованные или упакованные массивы, упакованные массивы литер - строки.
Простые состоят из одного значения базового типа . Составные из одного или нескольких значений не обязательно
скалярного типа. Для массива характерно , что все его компоненты однотипны.
Во многих задачах однотипность элементов естественна.
Пример:
Вычислить произведение квадратных матриц размером n x n.
<шапка>
CONS
N = 10;
TYPE
ar = array [1 .. N, 1 .. N] of integer;
VAR
a, b, c : ar;
i,j, k : integer;
BEGIN
<ввод данных для a и b>
for i:= 1 to N do
for j := 1 to N do
begin
c[i, j] := 0;
for k := 1 to N do
c[i, j] := c[i, j] + a[i, k] * b[k, j];
end;
<вывод c >
END.
Но в природе есть объекты, которые имеют неоднородные свойства, и описать их однотипной матрицей невозможно.
Пример:
Анкета сотрудника:
1)
2)
порядковый номер N - число;
ФИО - фамилия - строка;
имя - строка;
отчество - строка;
3) дата рождения - день - число от 1 до 31,
месяц - число от 1 до 12,
год - число от 1920 до 2000;
4) пол - 0 или 1.
Построить модель - анкету в форме матрицы данных одного типа вряд ли разумно, хотя при некоторой доли фантазии
возможно.
Поэтому в теории данных для моделирования объектов с различными по виду свойствами используют понятие структуры
данных (или агрегат данных).
К сожалению, термин структура данных имеет два смысла: широкий - он означает строение данного, отношение между
компонентами данного и узкий -данное обладает структурой.
Так, например, в широком смысле массив имеет двухмерную структуру, его данные расположены и по вертикале и по
горизонтали.
Анкета - сотрудник имеет иерархическую структуру:
АНКЕТА
- 0 уровень
1 уровень
N -порядковый
имя
2 уровень
фамилия
ФИО
отчество
дата
день
пол
месяц
год
Каждая компонента анкеты называется полем. Некоторые поля могут быть составными, тогда возникает следующий уровень.
Очевидно, что если поле i - уровня составное то его компоненты расположены на i + 1 уровне, а скалярное поле -i -го уровня
не имеет компонентов и, следовательно, для него отсутствует i + 1 уровень. Важно понять, что данные последних скалярных
уровней образуют совокупность данных, которое рассматривается как единое целое.
{5, Кураедов , Илья, Александрович, 01,01, 1967, 1} - анкета.
Некоторая группа данных, например {Кураедов , Илья, Александрович}- новое данное, т. е. в структуре существует
подструктура, но каждая структура - это совокупность скалярных данных входящих в структуру не только как непосредственные
компоненты, но как компоненты подструктур.
Таким образом, в широком смысле - акцент на структуру в смысле строения. В узком смысле - акцент на данное, имеющее
определенное строение. Понятие структуры данных в узком смысле применяют в языках программирования при описании
совокупности данных состоящих из неоднородных элементов. Так в языке СИ структура - название особого класса данных. В
Паскале для характеристики неоднородных данных применяют термин запись и надо сказать неудачно (т. к. запись - это
компонента файла), лучше говорить данные типа record.
В Паскале данные типа record относятся к записным типам, т. е. множествам, имеющих однородное строение компонентов,
но это строение не массивов, а записи.
Данные вида record подразделяются на типы record , каждый тип - бесконечное множество ( в практическом смысле).
Строение компонент множества задается описанием:
TYPE
имя_типа_вида_RECORD =
RECORD
список_имен_полей 1 : тип 1; 
список_имен_полей 2 : тип 2; секция
...
список_имен_полей n : тип n 
END;
В типе RECORD теоретически может и не быть секций, но на практике таких записей не применяют, поэтому будем считать
n  1. В секции записи может быть одно или несколько полей. Каждое поле задано именем - идентификатором ( в
синтаксическом плане), т. к. тип может быть любым
( кроме определяемого) и обязательно ранее определенный, то возможно
объявление одним типом многоуровневых записей с помощью одного оператора.
На практике желательно описать типi - явно, а затем использовать в RECORD ... END по имени, т. е. использовать идеологию
типизации языка Паскаль.
Пример:
{тип 3-го уровня}
NameType = packed array [ 1 .. 30];
DayType = 1 .. 31;
MonthType = 1 .. 12;
YearType = 1920 .. 2000;
{тип 2-го уровня }
FioType = record
fam: NameType;
nam: NameType;
otch: NameType
end;
DataType = record
day: DayType;
month: MonthType;
year: YearType
end;
{тип 1-го уровня }
AncetType = record
number: integer;
fio: FioType;
data: DataType;
pol: boolean
end;
Теперь объявим запись - массив записей:
VAR
AncetOne : AncetType;
AncetSot: array [ 1 .. quty] of AncetType;.
Вместо записи можно было использовать массивы, но тогда если бы нужно было бы переслать анкету полностью, то
пришлось бы указать несколько операторов “:=” , вместо одного.
Над данными класса record определены операции:
1)
:= - присваивания :
 данные  данные
AncetSot[k]:= AncetOn
одного типа record
AncetOne:=
AncetSot[k];
2) селекция - выделение поля записи “.”. Имеет два операнда : левый - имя записи, правый - имя поля в этой записи, при этом
если имя записи - на i уровне, то имя поля- на i + 1 уровне:
AncetOne.day - скаляр.
AncetOne. Data - структура,
AncetOne.fio - структура.
Инициализировать ( присвоить первоначальное значение) всех полей структуры можно только по полям скалярного вида:
read( AncetOne.day, AncetOne.month);
и т. д. , если нужно массив - то используется цикл.
Обратите внимание, что имена полей одного уровня в записи, если они принадлежат всей записи , должны быть уникальны по
любой ветви иерархии.
A
Bi  Bj , i j
B1
C21
B2
C22
B3
C23
B4
C41
C 42
Ci j  Ci k , j  k
C43
A
B1
B1
A.B1.B 1
B2
B2
A.B1.B2
B3
B3
B1
A.B1.B3
A.B3.B1
B2
B3
A.B3.B2 A.B3.B3
Любая скалярная компонента записи может быть использована там, где допускается применение однотипной скалярной
переменной.
Строение одного объекта можно моделировать разными способами, но все они дают разные типы для одного объекта, какой
способ использовать зависит от опыта.
Написать программу, которая заполняет запись данными из входного файла. При этом проверяем на корректность каждое
данное, и если оно не корректно, то выдаем сообщение и возвращаем ноль, иначе единицу.
Схема:
дано : запись - AncetOne и файл - input,
получить : читать данные из файла в запись, если они
корректны вернуть 1, иначе 0.
1
Program <заголовок>
TYPE
< описание AncetType>
VAR
AncetOne : AncetType;
CodOper : 0 .. 1;
CodStep: 0 .. 1;
j: 1 .. 30;
BEGIN
CodOper := 1;
<читать и проверять поле номер>
<вернуть CodStep>;
CodOper := CodOper * CodStep;
<читать и проверять поля подтруктуры fio>;
<вернуть CodStep>;
CodOper := CodOper * CodStep;
3
<читать и проверять поля подтруктуры data>;
<вернуть CodStep>;
CodOper := CodOper * CodStep :
4
<читать и проверять поле pol>;
< вернуть CodStep >;
CodOper := CodOper * CodStep;
END.
Логика программы продиктована структурой данных, т. е. структура данных определяет структуру программы, т. к. ранее ее
определяла структура функций программы. Такой подход к разработке алгоритма имеет свои преимущества перед
функциональным: прозрачная структура программы - и следовательно, управление программой тоже. Недостаток - малейшее
изменение в структуре данных и алгоритм изменяется серьезно, т. е. алгоритм программы чувствителен на изменения в структуре
исходных данных.
Возникает вопрос - каждую проблему в рассматриваемой программе можно сразу решить - нет, только 1 и 4.
CodStep:= 0;
read( input, AncetOne.pol);
if( AncetOne.pol = 0) or ( AncetOne.pol = 1)
then
CodStep:=1;
if CodStep = 0 then writeln(output, ‘пол не задан’);
Аналогично решается и 1 задача, но в остальных случаях это не тривиально и необходим аппарат подпрограмм.
2
Подпрограммы
Решение каждой задачи ( подзадачи) можно оформить в виде подпрограмм. Подпрограмма - часть программы, один раз
написанная, но выполняемая в программе один и более раз. Естественно, в программе подпрограмма должна выполняться хотя
бы один раз. Если она не выполняется ни разу при запуске программы, то она и не нужна.
Причины, обуславливающие применение подпрограмм в программе:
1). Размер программы. Если в программе достаточно много мест ( 2), где нужно выполнять одни и те же действия с
одними и теме же данными, или с другими данными, но с одной и той же структурой, то эти действия желательно оформить в
виде подпрограммы. Описать один раз в тексте программы, а там где в тексте программы идет ее вызов ( обращение к ней):
а) либо на этапе трансляции подставить текст подпрограммы;
б) либо передать управления модулю -подпрограммы.
Если применяется подстановка текста подпрограммы, то такую подпрограмму называют макроопределением , а сам процесс
подстановки - макроподстановка , результат подстановки - макрорасширение .
Многие языки программирования используют аппарат макроподстановки, особенно языки ассемблера. В стандарте Паскаля
макроподстановки не используются.
Пример:
subrout


 макроопеделение


м
а
к
р
о
в
ы
з
о
в
на этапе трансляции
program
макрорасширение
includ subrout
текст subrout
includ subro ut
Аппарат
макроподстановок
хорош
тем,
что
программист
имеет
возможность создать
большие по размеру
программы, используя
небольшие
тексты
определений макро и
исходной программы.
Непосредственная
подстановка в текст
программы макрорасширения, увеличивает текст и возможно в 2 - 4 раза, но скорость выполнения программы может быть
наивысшей.
Если использовать обращение к подпрограмме то:
текст subrout
макрорасширение
а) где-то вне программы или внутри нее имеется текст
подпрограммы;
program
subrout
subrout( ...)
- вызов подпрограмм
б)
где-то
вызов
подпрограммы
или
таких
вызовов может быть
несколько.
Выполняя,
операторы
программы в заданной последовательности, доходим до обращения к подпрограмме. В этом месте осуществляется “переход” на
начало подпрограммы subrout, и она выполняется в последовательности, определяемой логикой программы и исходными
данными. Как только будет достигнут оператор return или конец подпрограммы, то осуществляется “переход” на оператор ,
следующий за оператором вызова подпрограммы, и программа продолжает свое выполнение .
Здесь, в отличие от макроподстановки, текст не вставляется в тело программы в точке вызова подпрограммы. Вместо этого в
программе на этапе трансляции генерируются коды перехода на подпрограмму, и сама подпрограмма рассматривается как
некоторая отдельная часть программы, в которой наряду с обычными операторами имеются коды операторов возврата в
программу. Выигрыш по длине текста очевиден, проигрыш по времени - лишние операторы перехода к подпрограмме и обратно,
лишние операторы для организации интерфейса между программой и подпрограммой.
2). Упрощает процесс разработки сложных или больших по
объему программ. Разработка программ в
этом случае соответствует некоторой технологии программирования согласно которой, достаточно объемная задача программы
может быть выделена в самостоятельную программную единицу типа программа. Поэтому, если используется технология
программирования “сверху вниз” ( пошаговая детализация), то можно создать на первом уровне приемлемую по восприятию
программу, где достаточно сложные задачи решаются подпрограммами. Причем необязательно эти подпрограммы
разрабатывать немедленно. Их разработку можно отложить на более позднее время, заменив их для начало “заглушками” подпрограммы имитирующие основные результаты соответствующих подпрограмм. Если и сама подпрограмма окажется
сложной, процесс можно повторить.
1 уровень
заглушка1


заглушка 3

заглушка 2
2 уровень
заглушка 1







заглушка4
заглушка5
заглушка6

заглушка7
заглушка8
При технологии создания программ “снизу вверх” сначала разрабатывают подпрограммы самого низкого уровня, т. е. те,
которые не обращаются к другим подпрограммам. Затем созданные подпрограммы собираются в некоторый набор других
подпрограмм и т. д. пока не будет написана вся программа.
Для отладки подпрограмм низкого уровня используется драйвер - программа моделирующая внешнюю среду : исходные
данные, общие таблицы и т. п.
Бегло рассмотренные подходы к созданию программ с применением подпрограмм можно сравнить с подходом , когда
создается текст программы без подпрограмм. Начиная с некоторого момента программист теряет контроль за текстом
программы т. к. психологически и физически не в состоянии осмыслить большую по объему информацию, которая заложена в
программе. Другое дело, когда информация выделена в порции, она без лишнего напряжения воспринимается полностью. Здесь
программист как бы мыслит в терминах подпрограмм, а не их текстов, и следовательно, легче воспринимает программу в целом,
чем в мелких деталях.
3). Удобство поиска и исправления ошибок.
4). Проще процесс тестирования программ
и т. д.
Практически любой язык программирования имеет аппарат подпрограмм. Поэтому при изучении подпрограмм следует
обращать внимание на :
1)
2)
3)
.Синтаксис описания подпрограмм;
.Синтаксис обращения к подпрограммам;
.Область видимости переменных, используемых в подпрограмме. Это понятие означает некоторую часть подпрограммы,
где видима переменная в момент обращения к ней;
4)
. Способы передачи данных от программы к подпрограмме и обратно в момент вызова подпрограмм и возвращение
управления программе.
Для начала рассмотрим двухуровневую программу на языке Паскаль, т. е. программа на языке Паскаль состоит из основной
программы (главной) и подпрограммы. Согласно стандарту подпрограмма является внутренней, т. е. расположенной внутри
главной. По этой причине описание подпрограммы размещается после раздела описания переменных ( раздел VAR).
Структура описания подпрограммы
<Заголовок_программы>;
[<раздел меток>]
[<раздел констант>]
[<раздел типов>]
[<раздел переменных>]
[<раздел подпрограмм>]
<begin- блок >
Обратите внимание, что точно такую же структуру имеет описание программы, где begin-блок - begin
<список операторов>
end.
Таким образом, основную программу можно называть главной подпрограммой, она отличается от других подпрограмм на
синтаксическом уровне program <имя_программы>[ (<список_файлов>)], на семантическом уровне - к главной подпрограмме нельзя обращаться из
любой другой подпрограммы, т. к. к любой другой подпрограмме можно обращаться из любой, в том числе, из той же
подпрограммы и выполнение программы всегда начинается с главной.
Область действия имен
Рассматривая структуру подпрограммы, обратим внимание на begin-блок с точки зрения тех переменных, которые
допускается использовать в нем. Вспомним, что в программе (в главной подпрограмме) нельзя использовать имя, если оно
заранее не декларировано в одном из 5 разделов, если оно не является предопределенным. Обратим внимание, что внутри
begin-блока не могут появиться разделы, следовательно, все описания имен расположены между заголовком и begin-блока
процедуры в разделах.
Введем понятие блок - это часть подпрограммы, не содержащая заголовок подпрограммы, поэтому блок состоит из разделов
и begin-блока одной подпрограммы. Область объявления имени - это часть подпрограммы между точкой объявления имени и
последним end или это часть блока, где имя объявлено, начиная с точки объявления.
program
TYPE T1 = , T2 =
VAR x,y : real;
sub1 ...
TYPE T1 = , T3 =
VAR t,y;
...
begin
...
end
...
BEGIN
...
END
- область объявления
- область действия
Области объявления имен x, y - программа, t,
y - подпрограмма.
Область действия имени (не только
переменных, но и типов, констант, процедур и т.д.)
- это часть блока, где разрешено использовать имя
по первому его назначению.
Имя х в программе - переменная , ее область
действия совпадает с областью объявления и ее
можно использовать :
а) внутри begin-блока программы;
б) внутри begin-блока подпрограммы.
Имя y - в программе переменная, имя y - в подпрограмме переменная y , область действия y - только begin-блок
подпрограммы, область действия y - begin-блок программы, но не begin-блок подпрограммы. Это правило касается имен
констант, меток. Поскольку имена типов появляются в разделах, а не в begin-блоках, то правило определения области действия
сохраняется в общем случае , но в терминах разделов.
Имя sub1 доступно в любой точке программы, в любом begin-блоке подпрограммы и программы.
Рассмотрим особый случай:
program P;
...
subroutine P1;
subroutine P2;
subroutine P1;
subroutine P2;
P1
P2
P1
P2
subroutine P3;
P3
BEGIN
...
END
Область определения
1). В begin-блоке
программы
P
доступно обращение
к P1 и P2, а P1 и P3
внутри
P2
недоступны;
2). В begin-блоке
подпрограммы P1 доступна только P1;
3). В begin-блоке подпрограммы P2 допустимо обращение к P1, P3 и P2;
4).В begin-блоке подпрограммы P1 допустимо обращение к P1 , P2;
3).В begin-блоке подпрограммы P3 допустимо обращение к P1 ,P3, P2.
Имя подпрограммы можно использовать до описания, если есть опережающее описание.
Procedure A (<список_параметров> ); FORVARD;
...
procedure B (<список параметров>);
...
<вызов A>;
...
end;
procedure A; {параметры и тип результата не повторяется}
...
В языках программирования для характеристики имени (переменной) по месту расположения его в программе используют
термины локальное или внутреннее, глобальное или внешнее имя.
1). Если имя f объявлено в подпрограмме x, то оно локально
( или внутренние) в этой подпрограмме.
2). Если имя f локально в подпрограмме x , но используется и во внутренней по отношению к x подпрограмме y, то оно
внешнее по отношению к y.
3). Локальные имена главной подпрограммой являются глобальными
PROGRAM PR;
PR SP1
x z
SP11 SP12
VAR x, z ...
procedure SP1;
y z
VAR y, z ...
procedure SP11;
z
VAR z ...
begin
z:= x - y;
end;
procedure SP12;
t
VAR t ...
begin
z:= 6;
SP11;
z:= z + y;
end;
begin
y:=3;
z:= x+ y;
SP11; SP12;
end;
begin
x:=1;
z:=2;
SP1;
end.
именами
по отношению
ко
всем
подпрограмма
м, где это имя
используется.
Подпрогра
мма без
параметров
1.
Синтаксис
объявления:
procedure
<имя_процедур
ы>;
<блок>;
2.
Синтаксис обращения:
<имя_процедуры>;
Ранее мы рассмотрели трехуровневую подпрограмму - все подпрограммы без параметров.
Терминалогия:
SP1 - вызываемая из PR;
SP11 - вызываемая из SP1; 
SP2 - вызываемая из SP1;  вызывающая

Обмен данными между процедурами без параметров происходит через внешние переменные вызванной процедуры.
Определим значения всех переменных каждой процедуры:
PR:
x = 1, z = 2;
SP1:
y = 3, z = 4;
SP11:
z = -2 - локальная и недоступна после выхода;
SP12:
z = 6, z = z + y  z = 9;.
Таким образом вызываемая процедура “получает” от вызывающей внешние переменные, которые используются в расчетах,
могут быть модифицированы и в таком виде возвращаться назад. Следует заметить , что процедуры без параметров применяются
только для одних и тех же данных , т. е. данных с одним и тем же именем. При этом невозможно использовать процедуры для
таких же данных , но с другими именами. Для процедур с локальными переменными , но без параметров нужны дополнительные
операторы для переприсваивания. Неудобство налицо. Применение процедур без параметров в Паскале оправдано тогда, когда
это не приводит к усложнению восприятия программы. Наиболее оптимальный вариант - применение процедур с параметрами
без использования внешних или глобальных переменных.
Подпрограммы с параметрами
procedure<имя_процедуры>(<список_формальных_параметров>);
<блок>;
Список_формальных_параметров - это последовательность из одного или нескольких подсписков. Подсписки, если их несколько
разделены “;”.
<список_формальных_параметров>
<подсписок_формальных_параметров1>; ... ;
<подсписок_формальных_параметровn>
Каждый подсписок имеет структуру:
а) [VAR] < список_имен> : <имя_типа>,
б) <заголовок подпрограммы>.
Изучим сначала подсписок вида а). Подсписок имен содержит одно или несколько имен одного типа. Разделитель имен запятая.
Пример:
x1, x2, x3 : real
Var x4,x5 : char
Var x6: DayType
! После списка имен не может быть использован анонимный тип, только имя типа. Нет ограничения на типы параметров, т. е.
в качестве параметра можно использовать переменную любого типа. Поэтому в стандарте должен использоваться массивный
тип, а не его задание (т. е. тип_массив должен быть определен заранее) :
procedure InCorrect ( Var A : array [ 1 .. 10 ] of real);
Вызов процедуры:
<имя_подпрограммы>(<список_фактических_параметров>);
Если в списке фактических параметров несколько элементов, то они разделены “,”. Число элементов списка фактических
параметров равно общему числу формальных параметров. При этом одному и только одному формальному параметру
соответствует один и только один фактический, причем соответствие - по порядку следования элементов обоих списков. А так же
должны соответствовать:
а) по типу - фактические и формальные параметры совместимы по присваиванию,
б) по виду значения.
Если перед подсписком формальных параметров стоит слово Var в заголовке процедуры, то соответствующий ему список
фактических параметров должен состоять только из имен параметров-переменных.
Procedure PR3( Var f1,f2: char); - заголовок.
PR3 (v5.x1, v5.x2); - вызов.
Если же перед подсписком формальных параметров нет Var, то соответствующий список фактических параметров может
содержать выражения (константы - частный случай) - параметр-значение.
Procedure PR1 ( v1, v2, v3); - заголовок,
PR1 (v1,v2, v3); - вызов.
Слово Var в начале подсписка формальных параметров или его отсутствие определяет способы передачи значения между
формальными и фактическими параметрами, т. е. интерфейс между подпрограммами на уровне параметров.
Если отсутствует слово Var перед подсписком, то любому имени-параметру-значению в процедуре отводится память того
объема, которая требуется для значения указанного типа. Формальный параметр типа файл не может появиться в таком списке. В
момент вызова подпрограммы выполняются служебные подпрограммы, которые вычисляют значения выражений фактических
параметров и присваивают их формальным параметрам-значениям как начальные. По окончанию работы подпрограммы
значения формальных параметров, даже если они изменялись, назад не передаются соответствующим фактическим параметрам.
Такой способ передачи значений от фактических параметров к формальным называется передачей по значению .
Передача по значению - это передача в одну сторону - от фактических параметров к формальным.
Определение:
Если подпрограмма при своем выполнении изменяет переменную внешнею (или глобальную) , то она обладает побочным
эффектом.
Фактические параметры при передачи “по значению” защищены от побочного эффекта.
Передача значения между фактическим и формальным параметром-переменной двухсторонняя, т. е. фактические
переменные при передаче “по ссылке” не защищены от побочного эффекта.
PR1 ( v1, v2, v3)
procedure PR1( Var f1 : T1; f20,f21 : T2)
v1
адрес значение
v2
v3
значение значение
f1
адрес
значение
f20
f21
значение
`
При передаче
“по
ссылке”
в
процедуре
создается
полеуказатель
на
структуру данных
указанного типа. В
момент вызова подпрограммы адрес (а не значение ) фактического параметра передается в качестве значения формальному
параметру. Подпрограмма, выполняя операции с формальными параметрами, на деле выполняет их с фактическими,
следовательно если меняется значение формального, то меняется и значение фактического параметра.
Выводы:
1). Передача значений от фактического к формальному параметру может быть двух видов: “по значению” или “по ссылке”;
2). Если планируют передачу по значению, то в заголовке не используют Var, иначе (передача по ссылке) - Var;
3). Если формальному параметру-значению присвоить любым способом значение, то это не изменит соответствующий ему
фактический параметр, поэтому фактический параметр может быть любым выражением совместимым по присваиванию с
формальным параметром;
4). Если формальному параметру-переменной присвоить значение любым способом, то оно становится значением
фактического параметра;
5). Формальный параметр-значение может быть любого типа, кроме файлового или составного типа, содержащего файловый
тип.
Любой вызов процедуры при выполнении программы активизирует эту процедуру, это значит:
1). Все локальные переменные имеют память, но не имеют значения ;
2). Все глобальные переменные сохраняют память и значения перед началом выполнения первого оператора подпрограммы;
3). Устанавливается связь между фактическими и формальными параметрами по ссылке или по значению;
4). Управление передается первому оператору в begin-блоке процедуры.
Как только процедура закончила работу - дошла до конца begin-блока , то она деактивизируется :
1). Все локальные переменные теряют память, и они становятся недоступными;
2). Все внешние переменные сохраняют память и имеют те значения , которые могли получить при выполнении процедуры,
или значения сохраняются, если процедура их не меняла;
3). Фактические параметры, переданные по ссылке, сохраняют те значения, которые они получили (или не получили) при
выполнении процедуры;
4). Фактические параметры, переданные по значению , не изменяют своих значений;
5). Управление передается в вызывающую процедуру - к первому оператору, следующему после вызова процедуры.
Перейдем к рассмотрению подсписка формальных параметров процедуры вида б) (параметр-процедура).
Параметром процедуры может быть процедура. В этом случае список формальных параметров содержит подсписок в виде
параметр-процедуры. В этом подсписке только один параметр - процедура:
procedure<имя_параметра_процедуры>[(список_формальных_параметров)]
Фактический параметр - имя конкретной процедуры без параметров.
Пример:
...
procedure D ( x: real; Var y: real);
begin
y:= x * x;
end;
...
procedure REZ ( A: mas; Var rel: real; procedure CVAR (b: real;
Var c: real); n: integer);
VAR
i: integer;
r: real;
begin
r:= 0;
for i:= 1 to n do
begin
CVAR ( a[i], c);
r:= r +c ;
end;
...
REZ( b, rel, D, 10);
{обращение}
...
Ограничения на предописанные процедуры:
1). Принято считать, что все предописанные процедуры описаны на том же уровне, что и программа, т. е. их область
действия - вся программа;
2). Ни одна предописанная процедура не может быть фактическим параметром-процедурой;
3). Если описать внутри программы (подпрограммы) некоторую процедуру с именем Х  множеству имен
предописанных процедур, то из области действия предописанной процедуры выпадает область действия процедуры Х.
Пример:
program prim9;
VAR
a: real;
procedure read ( Var x: real);
begin
x:= x + 0.01;
end;
BEGIN
a:= 3;
read (a);
{ это не чтение a , a + 0.01}
END.
В этом примере стандартная процедура read отменена и заменена на свою read.
Функции
Функции - это подпрограммы, которые возвращают одно скалярное значение в вызывающую программу (подпрограмму)
через свое имя.
Во многом описание функции совпадает с описанием процедуры, но есть и отличия.
Заголовок функции:
function<имя_функции>[(<список_формальных_параметров>)]
:тип;
Если отсутствует список формальных параметров, то это функция без параметров, иначе - правила описания списка
параметров идентичны правилам процедуры.
Если нет опережающего описания ( forward), то имя функции обязательно - оно задает тип возвращаемого значения.
Пример:
function min (x, y : real) : real;
begin
if x> y then min:= x else min:= y;
end;
В теле функции обязательно должен присутствовать хотя бы один оператор присваивания вида:
<имя_функции> := <выражение>;
Функция завершает свою работу по достижению конца своего блока.
Обращение к функции называют указателем функции. Указатели употребляются только в выражениях. При этом тип функции
не должен противоречить той операции, в которой функция использована как операнд. Все остальное сказанное о procedure
верно и для function.
Рекурсивность подпрограмм
Рекурсивная подпрограмма (процедура, функция) предназначена для решения задачи путем многократного повторения
одного и того же метода. В некотором смысле рекурсивность похожа на цикл типа while<условие> do. Поэтому в рекурсии нужно
соблюдать два требования :
а) необходимо условие окончания рекурсии;
б) каждый шаг рекурсии должен приближать к условию окончания рекурсии.
Пример:
program prim10;
VAR
i: integer;
function X2 ( Var j: integer) : integer;
begin
if j -1 = 0 then X2 := 0 else X2 := X2( j - 1);
end;
BEGIN
writeln(‘укажите i’); readln (i);
if i> 0 then writeln (‘результат X2’, X2 (i))
else writeln (‘нужно положительное число’);
END.
Рассмотрим :
1-ый вызов - if i = 4  X2(4)
if 4 - 1 = 0 then ... else X2:= X2(3) - 2 ой;
2-ой вызов - X2(3)
if 3 - 1 = 0 then ... else X2:= X2(2) - 3-ий ;
3-ий вызов - X2(2)
if 2 - 1 = 0 then .. else X2:= X2(1) - 4-ый;
4-ый вызов - X2(1)
if 1 - 1 = 0 then X2:= 0;
и будет напечатано в программе - 0.
Каждая активация рекурсивной подпрограммы создает в оперативной памяти новые локальные переменные с
неопределенными значениями и формальные параметры согласно способу передачи значения и сохраняет старые локальные
переменные и фактические параметры, таким образом при рекурсии идет дополнительный расход оперативной памяти.
Множественный тип данных
Множественным типом данных называют множество всех подмножеств из элементов базового типа ( всегда ординального) с
набором операций:
а) объединение - “+”, пересечение - “*”, разность - “-”,
б) присваивания - “:=”,
в) отношения - x in X, = , <>, <=, =>
x X?, =, ,  , ,
г) конструктор множества - [].
Синтаксис объявления :
TYPE
<имя_типа> = [packed] set of <ординальный_тип>
В реализации количество элементов базового типа для создания множества ограничено - 256.
Пример:
TYPE
lit1 = set of [ 1 .. 16];
lit2 = set of [ 1 .. 10];
alf = set of [ ‘a’ .. ‘j’ ];
Множественные типы lit1, lit2, alf задают не одно множество из n элементов, а множество множеств из элементов базового
типа.
В синтаксисе даны все элементы базового типа, это влечет автоматическое определение 2k - множеств, где k - число
элементов базового типа для конструктора множества.
Рассмотрим lit1:
{0}, {1}, {2}, ... ,{16}

{1, 2}, {1, 3}, ... , {1, 16}

математическое
{2, 3}, {2, 4}, ... , {2, 16}

обозначение
...
{15, 16} 
множества
{1, 2, 3}
...

{1, 2, ... , 16}

В Паскале, если возникает необходимость указать множество не по имени, а перечислением, то применяют конструктор [].
0  [] , {1}  [1], {ai1, ai2, ... , ait }  [ ai1, ai2, ... , ait], где aij  базовому типу.
Конструктор применим к любой совокупности элементов базового типа
( но только базового типа), т. е. применять его к
элементам типа, не являющегося базовым ,для set of - ошибка.
В общем случае конструктор имеет вид:
[ константа [, выражение ][, выражение1 .. выражение2 ] ... ] , т. к.
элементы конструктора могут быть указаны явно - константа, неявно - выражение, диапазон - через значение левого и
правого выражений, Элементы конструктора должны быть совместимы по типу с использованным базовым типом.
Переменные множественного типа:
VAR
<имя_переменной>:<множественный_тип>
Поскольку не существует ограничений на использование множественного типа в массивах и записях, то он может быть типом
элементов массива или записи.
Операция присваивания:
<имя_переменной_множественного_типа>:=
VAR
x, y, t: lit1;
...
x:= [1, 2, 3];
y:= [1, 2, 9 .. 16];
t:= x;
...
Операции +, *, - :
t:= x + y  t = [1, 2, 3, 9 .. 16] ,
t:= x * y  t = [1, 2],
t:= x - y  t = [3].
Операции сравнения:
1)
in - <элемент_базового-типа>in<множественное_выражение>
25  Y?
25 in y  false
1 X?
1 in x  true
2) = и <> - сравнение двух множеств
x = y  false
x <> y  true
<выражение_множественного-типа>
3)
<= и >= (  и  )
x <= y ( x  y)  false
( x - [3] ) <= y  true
Приоритеты операций над множествами:
1-ый - +, *, -;
2-ой - =, < >, >=, <=
3-ий - in.
Пример:
Пусть список visit = { di1, di2, ... , d ik}, is  1, ... , 9, s  1, ... , k - список приглашенных гостей на вечер, а friend = { dj1, dj2, ... ,dr}, js 
1, ... , 9, s1, ... , r - список близких друзей.
Написать программу, которая читает элементы списков visit и friend и печатает список приглашенных и не приглашенных
близких друзей.
Program friends ;
TYPE
TypeName = [ d1, d2, ... , d9];
{перечисление через запятую имен}
SetTypeName = set of TypeName;
VAR
visit, freand : SetTypeName;
procedure inputname(var a: SetTypeName; text: string[60]);
VAR
lit: char;
BEGIN
writeln(output, text);
a:= [];
{пустое множество}
read(input, text); { укажите номер в списке }
while(lit <> ‘ ‘ ) do
begin
case lit of
‘1’ : a:= a + [d1];
‘2’ : a:= a + [d2];
...
‘9’ : a:= a + [d9]
end
read( input, lit);
end;
END
function result ( a, b : SetTypeName; var c: SetTypeName) : boolean;
BEGIN
c:= a - b;
if b <= a then resalt := true
else result := false;
END;
procedure outputname ( a: SetTypeName; text: string [60]);
VAR
i: integer;
BEGIN
writeln(text);
for i:= 1 to 9 do
case i of
1: if(d1 in a) then writeln(‘d1’);
2: if (d2 in a) then writeln (‘d2’);
...
9: if (d9 in a) then writeln(‘d9’)
end;
END;
BEGIN
inputname(visit, ‘Укажите список приглашенных’);
inputname( friend, ‘Укажите список друзей’);
if resalt( friend, visit, visit) then
writeln(‘все друзья приглашены’)
else
begin
writeln(‘ Не все друзья приглашены’);
outputname(visit, ‘это не приглашенные друзья’);
outputname( friend - visit, ‘ это приглашенные друзья‘);
end;
END.
Указатели (ссылки)
Как известно, любая переменная программы обладает помимо атрибутов, таких как имя, тип, область объявления, область
действия и обладает атрибутом памяти. Этот атрибут имеет три значения: статическая память, автоматическая память и
динамическая память. Вычисляется атрибут по контексту.
Если переменная является глобальной в программе, то ей назначается атрибут статической памяти, т. е. переменной
выделяется память после загрузки программы в оперативную память перед выполнением первого оператора. Эта память
сохраняется за переменной до конца выполнения программы.
Если переменная - локальная в подпрограмме, то ей назначается автоматическая память, т. е. переменной выделяется
память после передачи управления подпрограмме, но перед выполнением первого оператора. Память сохраняется за
переменной до тех пор пока процедура активна ( т. е. до конца ее выполнения), после выполнения подпрограммы память ,
назначенная автоматической переменной, становится свободной.
Третье значение памяти - динамическая память. Динамической памятью управляет программист под контролем
операционной системы.
Динамической памятью обладают переменные как локальные, так и глобальные ( здесь идет речь об атрибутах области
действия), но объявленные особым образом.
Динамические переменные не имеют имени. Память динамической переменной выделяется предопределенной процедурой
NEW , а освобождается - DISPOSE. Со значением динамической переменной или над динамической переменной допустимы все
операции, которые определены для типа переменной. Динамическая переменная обозначается посредством ссылочной
переменной или указателя.
Динамическая переменная представлена конструкцией - указатель ^.
Значением указателя является адрес памяти. Значением конструкции “указатель^” - значение динамической переменной при
этом “идентификатор”  “указатель^”.
Таким образом, любая динамическая переменная связана с указателем.
Указатель
Динамическая переменная

Часто возникает ситуация , когда связь теряется:

 мусор
Если в оперативной памяти имеется динамическая переменная, на которую не ссылается ни один указатель, то она
называется “мусором”. Наличие мусора в оперативной памяти лишает программу возможности использовать оперативную
память в полном объеме. Результат возникновения мусора - ошибки использования процедур NEW и DISPOSE .
Если указатель имеет значение, но отсутствует динамическая переменная, то такой указатель называется висячий.
Таким образом, любая динамическая переменная представлена указателем на нее.
Описание типа указателя:
TYPE
<имя_типа_указателя> = ^<тип_области>
или
TYPE
<имя_типа_указателя> = <имя_типа_указателя>

переименование типа
Тип_области - это тип динамической переменной может быть любым, в том числе типом указателя.
Переменная_указатель имеет описание:
VAR
<имя_переменной> : <имя_типа_указателя>;
Могут быть массивы указателей, указатели элементы структур и т. д.
Пример:
TYPE
x = (d1, d2, d3,d4);
TPX = ^x; - тип указателя на данные типа х
{значение переменной типа TPX - адрес динамических
VAR
vx: x;
px : TPX;
- в чем разница ?
vx - статическая или автоматическая переменная перечислимого типа,
vx
переменных типа Х }
d3
px - указатель на некоторую динамическую переменную типа х (сам указатель- не динамическая переменная).
px
vx?

?
Объявление указателя - объявление переменной ( переменной статической или автоматической), которая еще не
определена, т. е. память под px - выделена , но ее значение не определено, т. е. это висячая ссылка.
Px

?
Использовать неопределенное значение указателя нельзя - ошибка, при этом система может не отреагировать на отсутствие
значения, и следовательно, результат непредсказуем.
Есть константа nil - нулевое значение любого указателя.
Ее смысл :
1)
2)
указатель имеет значение;
она не указывает ни на одну переменную.
Лучше иметь указатель nil , чем указатель с неопределенным значением.
Первая операция : Px := nil; , но px^ -ошибка !
Вторая операция :
выделение памяти для динамической переменной - new(px);
px
px^

неопределенное

область для переменной типа х
Выделение памяти процедурой new(px) не означает , что динамическая переменная px^ имеет значение , скорее там “всякий
шум”.
После выделения памяти можно присваивать динамической переменной некоторое значение, например:
px^ := d4;
px
px^

d4
Можно сравнивать: if vx = px^ then ....
или
совместимы по типу
vx:= pred(px^); и получим vx = d3 и т. д., т. е. динамическая переменная уместна там, где допускается использование
статической или автоматической переменной того же типа . Если динамическая переменная нужна только для хранения одного
значения , независимо от его сложности , то лучше использовать статистическую переменную такой же структуры.
Третья операция:
освобождение памяти - DISPOSE (px);
px
?


считается неопределенной, т. е. вновь висячая ссылка, поэтому желательно px := nil; .
Пример:
new (px); px^ := d1;
n-ew (px); px^ := d2;
px
px^

d1
px^
d2
- мусор
DISPOSE ( px ); - уничтожение последнего размещения, а следующий DISPOSE ( px ) - ошибка.
Использовать указатели желательно в тех задачах, где требуется более чем одна динамическая переменная одного и того же
типа.
Пример:
Требуется создать список фамилий :
1.Сидоров;
2.
Аксенов;
...
N. Огарев.
Число элементов зависит от алгоритма и той информации, которую обрабатывает программа.
Определение:
Линейным однонаправленным списком называют последовательность однородных элементов данных (a1, a2, ..., ak), в
которой каждое ai указывает на следующий за ним элемент.
Вспомним, что массив (a1, a2, ..., ak) - совокупность однородных данных. Все его элементы упорядочены по индексу : за i-м
следует i+1 элемент массива, в оперативной памяти за i -м элементом, также следует i+1, наконец, в массиве число элементов
постоянно.
В списке за i-м элементом следует i+1, но это не означает, что в оперативной памяти за i-м следует i+1элемент.
Массив:
a1
a2
a3
a4
a5
a6
Список:
a1
a4
a2
a43
Расположим узлы списка ai = ( di , pi ) последовательно, чтобы иметь простую модель памяти для списка.
- голова
a1
d1
a2
d2
a3
d3
a4
d4
В списке:
1.
2.
3.
Каждый элемент списка наряду с информацией хранит данные о местоположении следующего;
Число элементов списка может быть 0, 1 и т. д., т. е. неограниченно;
Операции со списком :
1-ая группа - изменение геометрии списка:
а) включить новый узел,
б) удалить узел,
в) модифицировать узел;
2-ая группа - доступ к информации и ее
использование в задачах.
Списки используются в задачах при построении трансляторов с языков программирования, при решении задач на графах и т.
п.
В нашей задаче нужно хранить и использовать упорядоченный в лексикографическом порядке список фамилий, содержащий
от 0 до N фамилий.
При использовании массива много памяти будет израсходовано зря, поэтому перейдем к списку.
Список:
голова
Аксенов В. М.
Валентинов М. С.
указатель
на
начало узла
Сидоров С. М.
Включить Огарев М. С.
голова
Аксенов В. М.
Валентинов М. С.
Сидоров С. М.
Огарев М. С.
В естественном порядке:
голова
Аксенов В. М.
Валентинов М. С.
Огарев М. С.
Сидоров С. М.
Исключить Валентинов М. С.:
голова
Аксенов В. М.
Валентинов М. С.
Огарев М. С.
Сидоров С. М.
прежние
и
новые
связи.
Рассмотрим, как это записывается на Паскале, описав предварительно тип указателя и область, адресуемую указателем.
TYPE
TypePtrNode = ^TypeNode;
TypeNode = Record
fio: string [20];
next: TypePtrNode
End;
{Процедура включения фамилии в алфавитном (заданном ) порядке}
Procedure IncludeOrder (Var Head: TypePtrNode, fio: string[20]);
VAR
pcurr, pleft, pright: TypePtrNode;
BEGIN
{так как включение выполняется,
то выделим память заранее}
new( pcurr); pcurr^.fio:= fio;
pcurr^.next:= nil;
Схема памяти :
pcurr
fio
nil
{Список может быть пуст , тогда его Head if (Head = nil) then
begin
Head := pcurr; pcurr:= nil;
end;
Схема памяти:
nil}
head
pcurr

nil
fio
nil
else
begin
if ( Head^.next = nil )
из одного элемента}
begin
if ( fio>Head^.fio) then
{включить в конец}
then {список состоит
Схема памяти:
Head
pcurr
nil
fio
fio
nil
nil
нужно
head

pcurr
nil
fio
fio

nil
Begin
head^.next:= pcurr; pcurr := nil;
end
else
head

pcurr

fio
fio
nil
nil
head

pcurr
fio
nil
nil
fio

Begin
pcurr^.next:= head;
head:= pcurr;
pcurr:= nil;
end;
end
else
{в списке более одного элемента ; случай а : нужно включить элемент первым - похож на предыдущий вариант}
head

pcurr
fio1
fio2
fio3
fio4



nil

fio
nil
head

fio1
fio2
fio3
fio4


nil
fio


pcurr
nil
if ( fio <=
head^.fio) then
Begin
pcurr^.next:=
head;
head:= pcurr;
pcurr: = nil;
End
else
{просмотр списка в цикле}
pleft:= head; pright: = head^.next;
{известно, что включение или где то внутри, или где то в конце
while (pright<> nil ) do
if ( fio <= pright^.fio) then
{включить}
и left^.fio>fio}
head

fio1

fio2
fio3
fio4


nil
pcurr

pleft
pright

fio

nil
получим
head

fio1
fio2


pcurr
nil
fio3
fio4

nil
pleft
fio
nil
pright
nil

Begin
pcurr^.next:= pleft^.next;
pleft^.next:= pcurr;
pcurr:= nil; pleft:= nil; pright := nil;
End
{при выходе из цикла если pright =
nil ,то нужно включить в конец цикла}
pleft^.next:= pcurr; pcurr:= nil; pleft:= nil;
End;
Можно было бы и не обнулять pcurr, pleft, pright - по выходу из процедуры локальные переменные теряют память.
Переменная head , ее память, зависит от фактического параметра вызывающей процедуры, а не процедуры IncludOrder.
Элементы списка динамические, поэтому их память сохраняется при выходе из процедуры и доступна в вызывающей процедуре
через head.
Операции включения в список имеют много вариантов:
1)
2)
3)
включить элемент первым;
включить элемент последним;
включить элемент после узла ( если он имеется) с заданным адресомзависит от критерия включения.
Процедуры удаления узла из списка имеют, как и процедуры включения много вариантов:
1)
2)
3)
удалить первый;
удалить последний;
удалить после заданного адресом узла.
Рассмотрим алгоритм исключения элемента с сохранением заданного порядка.
Function DeleteOrder ( Var head: TypeNode; fio: string[20]): bolean;
{поиск}
Var
pcurr, pleft, pright: TypeNode;
Begin
if(head = nil) then
Begin
writeln(‘Список пуст, удалить нельзя’);
DeleteOrder:= false;
End
else
Begin
pleft:=head; pright:= head^.next;
{Удаление первого элемента и
возможно единственного}
if ( head^.fio = fio) then
Begin
pcurr := head; head := head^.next;
dispose(pcurr);
DeleteOrder := true;
End
head

fio1

pcurr
fio2
nil

{удаление
внутри или последнего}
else
while (pright<> nil ) do
if ( pright^.fio = fio )
then
Begin
pleft^.next: = pright^.next;
dispose(pright); pleft := nil;
DeleteOrder := true;
End
head

fio1
fio2


fio3
nil
pleft

pright

else
Begin
pleft:= pright;
pright := pright^.next;
End;
End;
Кроме линейного - однонаправленного списка можно выделить :
а) по способам обработки:
1)
первым вошел - первым вышел ( очередь), добавление элементов с одного конца, а удаление с
другого;
2)
п
ервым вошел - последним вышел ( стек), добавление и удаление элемента только с одного конца;
б) по способам организации:
1)
двунаправленный
head
2)
нарное дерево
б
и
корень
отец
сыновья
листья
ФАЙЛЫ
Файлы - однородная совокупность данных, имеющая единое имя, переменное число компонент . В процессе работы программы
для которой определены две операции: включить новую компоненту, “показать” некоторую компоненту.
Файловый тип - это множество файлов. Файлы одного типа - это совокупность данных так же одного типа. В отличие от других
типов данных в файловом типе не определены операции “:=”, “=” или “<>”. Над файлами определены операции:
1)
2)
3)
4)
открыть доступ к файлу;
включить элемент в файл;
получить доступ к элементу файла;
закрыть доступ к файлу.
Синтаксис типа файлов:
Type имя-файлового-типа = File of тип;
- где тип любой скалярный или структурированный, т. е. array, set, record, но
компонент структурированного данного не может быть файлом..
Синтаксис имени файла:
Var имя-файла: имя-файлового-типа;
Примеры:
Type
A1 = array [1 .. 10, 1 .. 16] of integer;
R2 = Record RA: A1; RC: char;
RAA: array [-1 .. 1] of char
end;
F1 = File of A!;
F2 = File of R2;
F3 = File of char;
F4 = File of real;
F5 = File of integer;
F6 = File of string [10];
и т. д.
и есть предопределенный тип
F7 : text  File of char , но это не совсем так text = file of char1, где char1 = CHAR  {маркер конца строки} . Данный маркер
может распознаваться только тремя процедурами readln(), writeln(), eoln() (*). Во всем остальном файлы типа text, не отличаются
от файлов других типов, с точки зрения операций создания и чтения.
Зачем нужны три процедуры (*) ?:
1.
По стандарту файлы типа text содержат данные типа integer, real, char , изображаемые цепочкой литер из класса char.
2.
Файлы типа text м. б. связаны с физическими файлами, которые состоят из последовательности физических записей. В конце
каждой записи есть специальный маркер - конца записи. Для того, чтобы логические файлы соответствовали физическим, и
порции информации ( несколько компонент файла text) соответствовали физическим записям , в файл типа text и добавили
логический конец записи eol. Знать коды этого маркера необязательно, т. к. есть три процедуры, распознающие этот символ.
Var
vf1: F!; vf2:F2; vf3: f3; vf4: F4; vf5: F5; vf6: F6; и т. д.
Если программа использует некоторую файловую переменную
( внешнюю по отношению к ней и созданную вне ее), то список
ее параметров должен содержать имя данного файла.
Например:
P1  VF1, VF2
то
program P1 (VF1, FV2);
<описание типов имен файлов VF!, VF2 >
...
Если подпрограмма использует файл-параметр, то он должен передаваться по ссылке.
Пример:
procedure SUBP (var x: F4);
....
SUBP(vf4); - при обращении.
Не каждому файлу программы соответствует вне программы физический файл . Логические файлы можно использовать точно так
же как массивы или списки для накопления, хранения и использования данных на период работы программы, не сопоставляя им
физические файлы. В этом случае в ОП файл организуется примерно, как и список, точное представление зависит от реализации.
Графика в Турбо Паскале.
Турбо Паскаль имеет 8 стандартных модулей, в которых содержатся все системные процедуры и функции.
Модуль - отдельно хранимая и независимо компилируемая единица, тогда как подпрограмма является структурным элементом
Паскаль-программы и не может существовать вне нее.
SYSTEM
DOS
CRT
PRINTER
OVERLAY
GRAPH
TURBO3
GPAPH3
SYSTEM - подключается автоматически к любой программе. CRT- обеспечивает практически полный спектр возможностей для доступа к
экрану дисплея в текстовом режиме. Кроме этого , в данный модуль включены средства чтения информации с клавиатуры ( включая
расширенные коды клавиш) и простейшего управления звонком.
Модуль GRAPH объединяет многочисленные программные средства управления графическим режимом работы дисплея. Данный
модуль обеспечивает использование всех возможностей наиболее распространенных типов дисплейных адаптеров CGA , EGA, VGA и
т.п. Библиотека содержит более 50 графических процедур и функций, как базовых ( рисование точек, линий, окружностей и т.п.), так и
расширяющих возможности базовых
( многоугольники, заполнение фигур, вывод текста и др.). Должен быть доступен
файл GRAPH.TPU и в рабочем каталоге должны находиться соответствующие графические драйверы ( файлы с расширением .BGI), а
если используется шрифт, то и файлы с расширением .CHR.
Любая программа должна иметь вид:
Program< имя_программы>;
USES Graph;
Var
grDriver, grMode, errCode: integer;
{Эти переменные используются процедурой InitGraph
для перехода в графический режим}
...
Begin
{тело программы}
...
grDriver:= Detect;
{автоматическое определение драйвера}
InitGraph(grDriver, grMode, ‘<путь к драйверу> ‘);
№ драйвера № режима
{ ‘d;\lang\tp\bgi}
errCode:=GraphResult;
if errCode = grOK then
Begin
...
{Режим открыт и Вы можете работать}
CloseGraph;
{Закрываем режим графики}
End
else
Begin
{Режим не удалось открыть . Почему?}
writeln(‘ Сообщите причину анализируя errCode‘ );
End;
End.
Переменная ErrCode сохраняет значение, полученное функцией GraphResult , которое может быть числом от 0 до 14. Безошибочная
работа - 0. Мнемоника константы - grOK.
Напишем теперь программу, позволяющую получить график функции в любой точке экрана (левый угол (0,0)).
Пусть заданы 4 натуральных числа u, v, l, h, которые определяют положение левого нижнего угла , ширину и высоту прямоугольной
области экрана, в которой должен быть построен график данной функции. Кроме этого задан отрезок a,b, на котором строится
график, и n - количество частей на которые нужно разбить a,b при построении графика. Условимся, что начало координат OXY всегда
будет совпадать с центром заданной прямоугольной области экрана. При построении графика будут использоваться значения данной
функции при x= a, a+s, a+2s, ... , b, где s=(b-a)/n. Рассмотрим абсолютные значения функции в этих точках. Пусть Fm - максимальное из
этих величин. Тогда график функции не выйдет за верхнюю и нижнюю границы данной прямоугольной области, если масштабный
множитель не превысит [h/(2Fm)]. Пусть Am= max(a, b) , тогда график не выйдет за левую и правую границы данной прямоугольной
области, если масштабный множитель не превосходит [l/(2Am)] , и если взять в качестве масштабного множителя минимум из данных
величин, то график функции целиком поместиться в данной прямоугольной области.
...
Var
u,v,l,h: integer;
s,a,b, x, y, fm, am: real;
m, i, n, u0, v0: integer;
xp, yp, xp1, yp1: integer;
Function F( x: real): real;
Begin
F:= ...
End;
Begin
read(u, v, l, h); read(a, b, n);
s:=(b-a)/n; x:=a; fm:= abs(F(x));
for i:= 1 to n do
Begin
x:= x+s;
if abs(F(x))> fm then fm:=abs(F(x));
End;
am:= abs(a);
if abs(b)> am then am := abs(b);
m:= trunc(h/(2* fm));
if m > trunc(l/(2* am)) then m:=trunc(l/(2* am ));
< ПЕРЕЙТИ В ГРАФИЧЕСКИЙ РЕЖИМ>
uo:=u+l div 2; v0:=v-h div 2;
line(u, v0, u+l, v0);
line(uo+m, v0, uo+m, vo-2);
line(uo,v, u0, v-h);
line(uo, vo-m, uo+2, vo-m);
x:=a; y:= F(x);
xp:= round(uo+m*x); yp:= round(vo-m*x);
for i:=1 to n do
Begin
x:= x+s; y:= F(x);
xp1:= round( u0+m*x);
yp1:= round( v0-m*x);
line(xp, yp, xp1, yp1);
xp:= xp1; yp:= yp1;
End;
End.
Значения u0,v0 - определяют начало координат. Здесь после определения масштабного множителя выполняется построение графика
функции F(x).
1/--страниц
Пожаловаться на содержимое документа