Москва 2010 г.
Составитель ст. преп. В.Г. Кулаков
УДК 004.451.9
Библиогр.: 6 назв.
2
Содержание Стр.
3
1. Язык программирования Паскаль
Язык Паскаль разработан Никлаусом Виртом в 1970 году как язык для
обучения процедурному программированию. Название языку дано в честь
выдающегося французского ученого и философа Блеза Паскаля, а в качестве
прототипа был использован алгоритмический язык Алгол-68.
Огромную роль в массовом распространении языка Паскаль сыграла компания
Borland International, которая создала Turbo-среду для разработки программного
обеспечения на персональных компьютерах. Turbo Pascal работал под управлением
операционных систем CP/M и MS-DOS на компьютерах с процессорами Intel серии
x86. Первая версия языка Turbo Pascal была выпущена в 1983 г.
В настоящее время язык Паскаль продолжает использоваться с целью обучения
основам алгоритмизации и программирования. Имеется множество различных
компиляторов, предназначенных для работы под управлением операционных систем
Windows, Unix и Linux.
2. Описание языка
Описание любого языка программирования должно включать описания
символов, элементарных конструкций, выражений и операторов.
Описание символов заключается в перечислении допустимых символов языка.
Под описанием элементарных конструкций понимают правила их образования.
Описание выражений – это правила образования любых выражений, имеющих
смысл в данном языке. Описание операторов состоит из рассмотрения всех типов
операторов, допустимых в языке.
Описание каждого элемента языка задается его синтаксисом и семантикой.
Синтаксические определения устанавливают правила построения элементов языка.
Семантика определяет смысл и правила использования тех элементов языка, для
которых были даны синтаксические определения.
Символы языка – это основные неделимые знаки, в терминах которых
пишутся все тексты на языке.
Элементарные конструкции – это минимальные единицы языка, имеющие
самостоятельный смысл. Они образуются из основных символов языка.
Выражение в алгоритмическом языке состоит из элементарных конструкций и
символов, оно задает правило вычисления некоторого значения.
Оператор задает полное описание некоторого действия, которое необходимо
выполнить. Для описания сложного действия может потребоваться группа
операторов. В этом случае операторы объединяются в составной оператор или блок.
Действия, заданные операторами, выполняются над данными. Предложения
алгоритмического языка, в которых даются сведения о типах данных, называются
описаниями или неисполняемыми операторами.
Объединенная единым алгоритмом совокупность описаний и операторов
образует программу на алгоритмическом языке.
4
3. Основные символы
4. Элементарные конструкции
5
Паскаль допускает запись целых чисел в шестнадцатеричной системе
счисления:
$40
$ABC0
Строки в языке Паскаль – это последовательность символов, записанная между
апострофами. Примеры строк:
'STRING'
'ПРОГРАММА'
Строка может содержать ноль и более символов из расширенного набора
символов кода ASCII. Строка, ничего не содержащая между апострофами,
называется нулевой или пустой. Два последовательных апострофа в строке
символов обозначают один символ – апостроф.
6
алгоритмических языков высокого уровня. В языке Паскаль существуют скалярные
и структурированные типы данных.
К скалярным относятся стандартные типы и типы, определяемые
пользователем.
Стандартные типы включают целые, действительные, символьный, логические
и адресный типы.
Типы, определяемые пользователем: перечисляемый и интервальный.
Структурированные типы имеют четыре разновидности: массивы, множества,
записи и файлы.
В языке Паскаль введены понятия эквивалентности и совместимости типов. Два
типа Т1 и Т2 являются эквивалентными, если Т1 и Т2 представляют собой одно и то
же имя типа или тип Т2 описан с использованием типа Т1 с помощью равенства.
Менее строгие ограничения определены совместимостью типов. Например,
типы являются совместимыми, если:
- они эквивалентны;
- являются оба либо целыми, либо действительными;
- один тип – интервальный, другой – его базовый;
- оба интервальные с общим базовым;
- один тип – строковый, другой – символьный.
Ограничения на совместимость типов можно обойти с помощью приведения
типов. Приведение типов позволяет рассматривать одну и ту же величину в памяти
ЭВМ как принадлежащую разным типам. Для этого используется конструкция
Имя_Типа(переменная или значение).
Например, Integer('Z') преобразует код символа Z в целое число.
7
Над целыми операндами можно выполнять следующие арифметические
операции:
+ – сложение,
- – вычитание,
* – умножение,
div – деление,
mod – получение остатка от деления.
Результат арифметической операции над целыми операндами есть величина
целого типа.
Операции отношения, примененные к целым операндам, дают результат
логического типа TRUE (истина) или FALSE (ложь).
В языке Паскаль имеются следующие операции отношения:
= – равенство,
<> – неравенство,
>= – больше или равно,
<= – меньше или равно,
> – больше,
< – меньше.
Действительные типы определяет те данные, которые реализуются
подмножеством действительных чисел, допустимых в данной ЭВМ.
Действительные типы данных перечислены в таблице 2.
8
Символьная константа может записываться в тексте программы тремя
способами:
- как один символ, заключенный в апострофы (например, 'A');
- с помощью конструкции вида #K, где K – код соответствующего символа, при
этом значение K должно находиться в пределах 0 … 255;
- с помощью конструкции вида ^C, где C – код соответствующего управляющего
символа, при этом значение C должно быть на 64 больше кода управляющего
символа.
К величинам символьного типа применимы все операции отношения.
Адресный тип (Pointer) определяет переменные, которые могут содержать
значения адресов данных. Для хранения адреса требуются два слова (4 байта), одно
из них определяет сегмент, второе – смещение. Для получения значения адреса
какой-либо переменной введена унарная операция @.
7. Константы
9
8. Перечисляемый тип данных
10
При использовании интервальных типов в программе может осуществляться
контроль за тем, чтобы значения переменных не выходили за границы, введенные в
описании типа.
10. Переменные
11
Любой оператор в программе может быть помечен меткой. В качестве метки
используются произвольные целые без знака, содержащие не более четырех цифр,
либо имена. Метка ставится перед оператором и отделяется от него двоеточием. Все
метки, используемые в программе, должны быть перечислены в разделе описания
меток, например:
label 3, 471, 29, Quit;
Описание констант позволяет использовать имена как синонимы констант, их
необходимо определить в разделе описаний констант:
const K= 1024; MAX= 16384;
В разделе описания переменных необходимо определить тип всех переменных,
используемых в программе:
var P,Q,R: integer;
A,B: char;
F1,F2: boolean;
Описание типов, процедур и функций будет рассмотрено ниже. Отдельные
разделы описаний могут отсутствовать, но следует помнить, что должны быть
обязательно описаны все компоненты программы.
Раздел операторов представляет собой составной оператор, который содержит
между служебными словами begin и end последовательность операторов. Операторы
отделяются друг от друга точкой с запятой. Текст программы заканчивается точкой.
Кроме описаний и операторов программа может содержать комментарии, которые
представляют собой произвольную последовательность символов, расположенную
между открывающей фигурной скобкой и закрывающей фигурной скобкой.
12. Выражения
13. Операторы
12
Оператор Паскаля состоит из выражений. Выражения оператора могут состоять
из операндов и операций. Обычно в выражениях выполняется сравнение либо
арифметические, логические или булевские операции.
Оператор присваивания состоит из двоеточия и знака равенства:
:=
Справа от оператора записывают выражение, слева указывают имя переменной,
которой присваивается значение выражения:
имя:= выражение;
Примеры:
x:=1;
a:=5.02;
b:=32*a+Sin(x);
S:='Иван Иваныч';
Тип переменной и тип выражения должны совпадать кроме случая, когда
выражение относится к целому типу, а переменная – к действительному. При этом
происходит преобразование значения выражения к действительному типу.
Составной оператор начинается с ключевого begin, за которым следуют
операторы языка Паскаль, и заканчивается ключевым словом end:
begin операторы end;
Составной оператор объединяет операторы в группу. Этим оператором
начинается и заканчивается раздел выполнения основной программы или
подпрограммы. После последнего оператора end основной программы ставится
точка.
Для ввода исходных данных используются операторы ввода:
Read(A1, A2, … AK);
ReadLn(A1, A2, … AK);
ReadLn;
Первый из них реализует чтение К значений исходных данных и присваивание
этих значений переменным А1, А2, …, АК. Второй оператор выполняет чтение К
значений исходных данных, пропуск остальных значений до начала следующей
строки, присваивание считанных значений переменным А1, А2, …, АК. Третий
оператор выполняет пропуск строки данных.
При вводе исходных данных происходит преобразование из внешней формы
представления во внутреннюю, определяемую типом переменных. Переменные,
образующие список ввода, могут принадлежать к целому, действительному или
символьному типам. Чтение данных логического типа в языке Паскаль не
предусмотрено.
Операторы ввода при чтении значений переменных целого и действительного
типа пропускает пробелы, предшествующие числу. В то же время эти операторы не
пропускают пробелов, предшествующих значениям символьных переменных, так
как пробелы являются равноправными символами строк. Пример записи операторов
ввода:
var V, S: Real;
W, J: Integer;
C, D: Char;
13
...
Read(V, S, W, J);
Read(C, D);
Значения исходных данных могут отделяться друг от друга пробелами и
нажатием клавиш табуляции и Enter.
Для вывода результатов работы программы на экран используются операторы
вывода:
Write(A1, A2,… AK);
WriteLn(A1, A2,… AK);
WriteLn;
Первый из этих операторов реализует вывод значений переменных А1, А2, …,
АК в строку экрана. Второй оператор выполняет вывод значений переменных А1,
А2, …, АК и переход к началу следующей строки. Третий оператор выполняет
пропуск строки и переход к началу следующей строки.
Переменные, составляющие список вывода, могут относиться к целому,
действительному, символьному или булевскому типам. В качестве элемента списка
вывода кроме имен переменных могут использоваться выражения и строки.
Вывод каждого значения в строку экрана происходит в соответствии с шириной
поля вывода, определяемой конкретной реализацией языка. Форма представления
значений в поле вывода соответствует типу переменных.
Оператор вывода позволяет задать ширину поля вывода для каждого элемента
списка вывода. В этом случае элемент списка вывода имеет вид А:К, где А –
выражение или строка, К – выражение либо константа целого типа. Если выводимое
значение занимает в поле вывода меньше позиций, чем К, то перед этим значением
располагаются пробелы. Если выводимое значение не помещается в ширину поля К,
то для этого значения будет отведено необходимое количество позиций. Для
величин действительного типа элемент списка вывода может иметь вид А:К:М, где
А – переменная или выражение действительного типа, К – ширина поля вывода, М –
число цифр дробной части выводимого значения. К и М – выражения или константы
целого типа. В этом случае действительные значения выводятся в форме
десятичного числа с фиксированной точкой.
Пример записи операторов вывода:
var A, B: real;
P, Q: integer;
...
writeln(A, B:10:2);
writeln(P, Q:8);
Пример простой программы, которая запрашивает у пользователя три числа,
вычисляет сумму введенных чисел и выводит полученный результат на экран:
PROGRAM SUM;
var A,B,C,S: real;
begin
read(A,B,C);
S:= A+B+C;
writeln('S=', S:8:3)
end.
14
Битовая арифметика введена в Паскаль для обеспечения возможности работы
с двоичными разрядами (битами). Операции битовой арифметики применимы
только к целым типам.
Первая группа битовых операций – логические операции not (отрицание), and
(И), or (ИЛИ) и xor (исключающее ИЛИ). Операция not является унарной, она
изменяет каждый разряд целого числа на обратный. Операции and, or и xor –
бинарные, операнды этих операций – целые величины одинаковой длины. Операции
выполняются поочередно над всеми двоичными разрядами операндов.
Вторая группа операций – это операции сдвига влево shl и вправо shr:
I shl N
I shr N
Эти операции сдвигают двоичную последовательность значения I влево или
вправо на N двоичных разрядов. При этом биты, уходящие за пределы разрядной
сетки, теряются, а освободившиеся двоичные разряды заполняются нулями. При
сдвиге вправо отрицательных значений освободившиеся разряды заполняются
единицами.
Условный оператор включает в себя операторы, которые выполняются или не
выполняются в зависимости от записанного в операторе условия. Оператор имеет
вид:
If условие Then оператор1 Else оператор2;
Условие – выражение логического типа. Оператор1 выполняется, если условие
верно (TRUE); оператор2 выполняется, если условие не верно (FALSE).
В условном операторе может отсутствовать блок Else т. е. условный оператор
может иметь вид:
If условие Then оператор;
Поскольку операции сравнения имеют низший приоритет, то при проверке
нескольких условий эти условия заключаются в скобки.
Условный оператор может применяться для идентификации объекта по
определенным признакам составляющих его элементов. Если объектом является
треугольник, то элементами могут быть три его угла a, b и c:
if (a>90) or (b>90) or (c>90)
then writeln('Треугольник - тупоугольный');
if (a<90) and (b <90) and (c<90)
then writeln('Треугольник - остроугольный');
В случае зависимых событий могут применяться вложенные условные
операторы. Во вложенных условных операторах служебное слово else относится к
ближайшей конструкции If.
Оператор цикла с параметром применяется при выполнении расчетов или
других действий, повторяющихся определенное количество раз. Оператор для цикла
с инкрементом параметра имеет вид:
For i:= N1 To N2 Do оператор;
Оператор для цикла с декрементом имеет вид:
For i:= N1 DownTo N2 Do оператор;
Здесь i – параметр цикла, N1– начальное значение параметр, N2 – конечное
значение. N1 и N2 могут быть константами, переменными или выражениями.
15
Цикл с инкрементом выполняется при условии N1 <= N2. Значение параметра i
возрастает с шагом 1 от N1 до N2.
Цикл с декрементом выполняется при условии N1 >= N2. Значение параметра i
убывает с шагом 1 от N1 до N2.
В операторе цикла не разрешается присваивать параметру цикла какое-либо
значение. После окончания цикла значение параметра не определено.
Оператор цикла часто применяется для суммирования некоторой
последовательности чисел. Сумма членов последовательности величин a1, a2, …, aN
называется конечной суммой. Приведем пример расчета суммы последовательности
SN = 12 + 32 + 52 + … + (2*N–1)2:
PROGRAM SUM_K;
var a,S,i,N: word;
begin
write('Введите число членов суммы N=');
readln(N);
S:= 0;
{Цикл суммирования}
for i:= 1 to N do begin
a:= Sqr(2*i-1);
S:= S+a
end;
writeln('Конечная сумма S=', S);
writeln('Нажмите Enter'); readln
end.
В Паскале применяются два оператора цикла с условием.
Цикл с предусловием выполняет проверку условия перед каждым
выполнением оператора:
While условие Do оператор;
Цикл с постусловием выполняет проверку условия после каждого выполнения
оператора:
Repeat операторы Until условие;
Условие – это выражение логического типа (Boolean).
В цикле While оператор выполняется если условие верно (True), если условие
ложно (False), то цикл заканчивается, т. е. цикл While повторяется пока выполняется
условие. Цикл While начинается проверкой условия, поэтому, если начальное
условие ложно, то оператор не выполняется ни разу. Для включения в тело цикла
нескольких операторов применяется составной оператор.
Цикл Repeat повторяется, если условие ложно, и заканчивается, если условие
верно, т. е. цикл повторяется до выполнения условия. Цикл Repeat заканчивается
проверкой условия, поэтому операторы выполняются не менее одного раза. В теле
цикла может записываться более одного оператора.
Циклы с условием обычно используются в тех случаях, если количество
повторений блока операторов заранее не известно, например, при расчете суммы
членов бесконечного ряда с заданной погрешностью.
16
Если члены ряда являются функциями аргумента x, то ряд называется
функциональным. Приведем пример расчета значения функции y=sin(x) с
использованием представления функции в виде ряда:
y = x – x3/3! + … + (–1)(N+1)x(2*N+1)/(2*N+1)! + … ,
где a0=x, aN=k*aN-1, k=(–x2)/(2*N*(2*N+1)), N=0, 1, 2, …
Каждый член ряда аN при N>0 можно получить умножением предыдущего
члена ряда aN-1 на коэффициент k. Приближенное значение функции находится как
частичная сумма N членов ряда. Погрешность вычисления значения функции
зависит от количества членов ряда и значения х, поэтому расчет заканчивается при
|aN| < eps, где eps – допустимая погрешность.
PROGRAM SIN_R;
var y,x,eps,a,k: real;
n: word;
begin
write('Введите значение x='); readln(x);
write('Введите значение погрешности еps='); readln(eps);
writeln;
n:= 0;
a:= x; {первый член ряда}
y:= a; {первая частичная сумма ряда}
while abs(a)>eps do begin
n:= n+1;
k:= -x*x/(2*n*(2*n+1));
a:= a*k;
y:= y+a;
writeln('Приближенное значение функции ',y:11:8,' при n=',n)
end;
writeln('Нажмите Enter'); readln;
end.
Операторы ограничения и прерывания цикла применяются внутри
операторов цикла. Операторы имеют вид:
Continue; – ограничение цикла,
Break; – прерывание цикла.
Действие оператора Continue заключается в передаче управления на начало
цикла, при этом проверяется условие выхода из цикла. Действие оператора Break
заключается в передаче управления оператору, следующему за последним
оператором цикла. Во вложенных циклах операторы Continue и Break действуют
только на цикл в котором они записаны.
Оператор перехода служит для передачи управления помеченному оператору
и имеет вид:
Goto метка;
Обычно операторы в программе выполняются в том порядке, в каком они
записаны. Оператор перехода прерывает естественный порядок выполнения
программы и указывает, что дальнейшее выполнение должно продолжаться,
начиная с оператора, помеченного указанной меткой.
17
Меткой может быть целое положительное число или идентификатор. Меткой
помечается оператор, которому передается управление, например:
M1: write('Введите x>=0'); readln(x); if x<0 then goto M1;
Метки описываются в разделе описания, например:
label M1;
Метка не может стоять перед оператором Begin.
Оператор варианта Case состоит из выражения (переключателя) и списка
операторов, каждому из которых предшествует одна или более констант (они
называются константами выбора) или ключевое слово else:
case выражение of
константа_1: оператор;
…
константа_N: оператор;
else оператор;
end;
Переключатель должен иметь тип Byte, Shortint, Integer, Boolean или Char;
строковый и длинный целый типы являются недопустимыми. Константы выбора
должны быть уникальными и иметь тип, совместимый с типом переключателя.
Оператор варианта приводит к выполнению оператора, которому предшествует
константа выбора, равная значению переключателя или диапазону выбора, в
котором находится значение переключателя. Если такой константы выбора или
такого диапазона выбора не существует и присутствует ветвь else, то выполнятся
оператор, следующий за ключевым словом else. Если же ветвь else отсутствует, то
никакой оператор не выполняется.
Приведем пример оператора варианта:
case I of
0, 2, 4, 6, 8: writeln('Четная цифра');
1, 3, 5, 7, 9: writeln('Нечетная цифра');
end;
18
которого базовым является целый тип, могут принимать отрицательные, нулевое и
положительные значения.
Чтобы использовать массив в программе на Паскале, требуется, во первых,
определить параметры массива (тип индекса и тип компонента), во-вторых, описать
переменную-массив. Описание типа массива задается следующим образом:
имя типа = array [список типов] of тип;
Тип индекса в определении типа заключается в квадратные скобки после
служебного слова array, а тип компонента задается после служебного слова of.
Пример:
type wordtype = array[1..12] of char;
var Name: wordtype;
Второй способ описания массива – упрощенный: тип массива определяется
неявно вместе с описанием переменной. Пример:
var Name: array[1..12] of char;
Двумерные массивы хранятся в памяти ЭВМ по строкам. Первый индекс
определяет номер строки, второй – номер столбца. Пример описания матрицы целых
чисел, состоящей из 5 строк и 10 столбцов:
var Matrix: array[1..5, 1..10] of integer;
Пример описания трехмерного массива:
var B: array[1..5, 1..10, 1..8] of integer;
В операторной части программы один массив может быть присвоен другому,
если их типы идентичны, например:
R1:=Z;
Для ввода или вывода массива в список ввода или вывода помещается
переменная с индексом, а операторы ввода или вывода выполняются в цикле.
Инициализация массивов выполняется с помощью типизированных констант,
например:
type Dim10 = array[1..10] of real;
const M10: Dim10 = (0, 2.1, 4, 5.65, 6.1, 6.7, 7.2, 8, 8.7, 9.3);
При инициализации двумерных массивов значения компонент каждого из
входящих в него одномерных массивов записывается в скобках:
type Dim3x2 = array[1..3,1..2] of integer;
const M3x2: Dim3x2 = ((1,2)(3,4)(5,6));
Особое место в языке Паскаль занимают массивы символов. Стандартный
Паскаль допускает два способа хранения символьных массивов в памяти ЭВМ:
распакованный и упакованный. Распакованные массивы символов хранятся в
памяти ЭВМ по одному символу в машинном слове, упакованные – по одному
символу в байте. При описании упакованного массива символов используют
служебное слово PACKED, например:
var Mas: packed array[1..20] of char;
Описание распакованного массива символов имеет вид:
var M: array[1..20] of char;
Для преобразования символьного массива из распакованной формы в
упакованную и наоборот, из упакованной в распакованную, в язык Паскаль введены
две стандартные функции Pack и UnPack.
19
Упакованный массив символов образует символьную строку. Символьная
строка может быть либо строковой константой, либо строковой переменной.
Строковая константа, или строка, представляет собой совокупность символов,
заключенную в апострофы. Строка – это элементарная конструкция языка Паскаль.
Строковые константы могут входить в состав выражений. Как и числовые
константы, они могут быть описаны в разделе описания констант.
Строковые переменные – это одномерные упакованные массивы символов, для
описания которых введен тип String. Например, если строка содержит до 30
символов, ее тип будет определен как
type s= String[30];
Длина строки не может содержать более, чем 255 символов.
В Турбо Паскале определено понятие строки переменной длины, в этом
случае ее описание задается как
type s= String;
Тип String без указания длины совместим со всеми типами строк.
Особенностью строковых переменных является то, что к ним можно
обращаться как к скалярным переменным, так и к массивам. Во втором случае
применяется конструкция «переменная с индексом», что обеспечивает доступ к
отдельным символам строки. При этом нижняя граница индекса равна 1. Отдельный
символ строки совместим с типом Char.
В памяти ЭВМ строка занимает количество байтов, на единицу большее ее
длины. Нулевой байт строки содержит ее длину.
Для строк определены операции присваивания, слияния (конкатенации) и
сравнения.
Для сравнения строк применяются все операции отношения. Сравнение строк
происходит посимвольно, начиная с первого символа. Строки равны, если имеют
одинаковую длину и посимвольно эквивалентны.
Строки могут быть элементами списка ввода – вывода, при этом записывается
имя строки без индекса.
При вводе строковых переменных количество вводимых символов может быть
меньше, чем длина строки. В этом случае вводимые символы размещаются с начала
строки, а оставшиеся байты заполняются пробелами. Если количество вводимых
символов превышает длину строки, лишние символы отбрасываются.
Для того, чтобы присоединить к концу строки символ или другую строку,
можно использовать операцию сложения.
Для определения длины строки предназначена стандартную функцию Length,
которая получает в качестве аргумента строку (значение типа String), а возвращает
длину строки (значение типа Integer).
Пример программы, которая присоединяет одну строку к концу другой:
PROGRAM SCON;
var s1, s2: string;
k: integer;
begin
{ Ввод первой строки }
write(’Введите строку: ’);
20
readln(s1);
{ Вычисление и вывод на экран длины введенной строки }
k:= length(s1);
writeln(’Длина строки = ’, k, ’ символов’);
{ Ввод второй строки }
write(’Введите слово: ’);
readln(s2);
{ Объединение строк и вывод результата }
s1:= s1 + ’ ’ + s2;
writeln(’Результат: ’, s1)
end.
Инициализация строк может производиться с помощью типизированных
констант:
const sName: String[9]= 'IBM PC/AT';
21
В подпрограммах могут использоваться:
- параметры, передаваемые по значению;
- параметры, передаваемые по ссылке;
- параметры-процедуры;
- параметры-функции;
- нетипизированные параметры
Параметры, передаваемые по значению, используются для передачи данных в
процедуру или функцию: в подпрограмму передается копия каждого формального
параметра, даже если в их роли выступают переменные или константы. В процессе
выполнения подпрограммы значения таких параметров могут изменяться как
угодно, но это никак не повлияет на значения фактических параметров.
Параметры, передаваемые по ссылке, используются для хранения результатов
выполнения процедуры или функции. Они перечисляются после слова var с
обязательным указанием типа. Каждому такому параметру может соответствовать
только фактический параметр в виде переменной. При вызове подпрограммы ей
передается ссылка на переменную.
Нетипизированные параметры передаются по ссылке, однако для них не
указывается тип. В этом случае в подпрограмму можно передавать переменные-
параметры любого типа, а на программиста возлагается ответственность за
корректное приведение типов внутри подпрограммы.
Стандартные подпрограммы являются частью языка программирования. Все
стандартные процедуры и функции реализованы в специальных библиотечных
модулях, которые подключаются сразу после заголовка программы с помощью
оператора uses.
К стандартным относятся следующие модули:
System – модуль, обеспечивающий работу всей системы Турбо Паскаля (всегда
подключается автоматически и в разделе uses не указывается).
Crt – модуль управления дисплеем и клавиатурой.
Dos – модуль, позволяющий использовать прерывания MS DOS.
Graph – модуль для работы с монитором в графическом режиме.
Printer – модуль для организации печати на принтере.
Пользовательские подпрограммы создаются самим пользователем и
объявляются после объявления переменных перед первым оператором begin
программы. Если процедура объявлена в отдельном модуле, для обращения к ней
необходимо дать ссылку на модуль в разделе uses.
Для объявления процедуры используется следующая конструкция:
procedure имя(параметры);
const объявления констант;
type объявления типов;
var объявления переменных;
объявления других процедур и функций;
begin
операторы
end;
22
Для вызова процедуры необходимо указать ее имя, а затем – перечень
фактических параметров в круглых скобках.
Например, если объявлена процедура
procedure ShowMessage(s: string, len: integer);
begin
writeln(s:len); {len – ширина поля вывода}
end;
то для ее вызова должен использоваться оператор следующего вида:
ShowMessage(’Hello’, 10);
Если процедура объявлена без списка параметров, то она вызывается только по
имени, без использования круглых скобок.
Например, если объявлена процедура
procedure ShowHello;
begin
writeln(’Hello’);
end;
для ее вызова должен использоваться оператор:
ShowHello;
Для объявления функции используется конструкция:
function имя(параметры): тип результата;
const объявления констант;
type объявления типов;
var объявления переменных;
объявления других процедур и функций;
begin
операторы
end;
В теле функции должен быть как минимум один оператор, в котором имени
функции присваивается возвращаемое значение. В точку вызова функции
возвращается результат последнего присваивания.
Вызов функции может быть частью выражения, так как функция возвращает
некоторое значение. Для вызова необходимо указать имя функции и перечень
параметров в круглых скобках.
Константы, типы, переменные, подпрограммы, объявленные внутри
подпрограммы, доступны только внутри этой подпрограммы. В то же время любые
глобальные (объявленные в заголовке программы) константы, типы, переменные,
процедуры и функции доступны в любой точке программы, включая все
подпрограммы.
23
Описание: function ArcTan(x: Real): Real;
Арктангенс используется для получения других обратных тригонометрических
функций:
x
arcsin( x) arctg ,
1 x
2
1 x2
arccos( x) arctg ,
x
1
arcctg ( x) atctg .
x
Функция Cos возвращает косинус аргумента.
Описание: function Cos(x: Real): Real;
Функция Exp возвращает экспоненциальное значение аргумента.
Описание: function Exp(x: Real): Real;
Функция Ln возвращает натуральный логарифм аргумента.
Описание: function Ln(x: Real): Real;
Пример:
PROGRAM LOG;
var Q: real;
begin
write('Введите Q: '); readln(Q);
writeln('Логарифм Q: ',ln(Q):8:2)
end.
Функция Pi возвращает значение числа Pi.
Описание: function Pi: Real;
Функция Random возвращает случайное число.
Описание: function Random[(Range: Word)];
Примечания: Если параметр Range (диапазон) не задан, то результатом будет
вещественное число x в диапазоне 0 <= х < 1. Если задан параметр Range, то он
должен представлять собой выражение целого типа, а результатом будет случайное
число длиной в слово в диапазоне 0 <= х < N, где N – значение, заданное
параметром Range.
Процедура Randomize инициализирует встроенный генератор случайных
чисел случайным значением.
Описание: procedure Randomize;
Пример:
PROGRAM RNDM;
begin
Randomize;
Writeln(’Случайное число от 0 до 1: ’, Random));
Writeln(’Случайное число от 0 до 100: ’, Random(101));
end.
24
Функция Round округляет значение вещественного типа до ближайшего целого
числа.
Описание: function Round(x: Real): Longint;
Функция Sin возвращает синус аргумента.
Описание: function Sin(x: Real): Real;
Функция Sqr возвращает квадрат аргумента.
Описание: function Sqr(x);
Тип результата соответствует типу параметра.
Функция Sqrt возвращает квадратный корень аргумента.
Описание: function Sqrt(x: Real): Real;
Функция Trunc усекает значение вещественного типа до значения целого типа.
Описание: function Trunc(x: Real): Longint;
17. Рекурсия
25
19. Модули
26
В том случае, если имена переменных в интерфейсной части модуля и в
программе, использующей этот модуль, совпадают, обращение будет происходить к
переменной, описанной в программе. Для обращения к переменной, описанной в
модуле, необходимо применить составное имя, состоящее из имени модуля и имени
переменной, разделенных точкой.
Например, пусть имеется модуль, в котором описана переменная К:
unit M;
interface
var K: Integer;
implementation
.................
end.
Пусть программа, использующая этот модуль, также содержит переменную К:
Program P;
uses M;
var K: Char;
begin
............
end.
Для того, чтобы в программе P иметь доступ к переменной K из модуля M,
необходимо задать составное имя M.K.
Использование составных имен применяется не только к именам переменных, а
ко всем именам, описанным в интерфейсной части модуля.
Если в модуле имеется раздел инициализации, то операторы из этого раздела
будут выполнены перед началом выполнения программы, в которой используется
этот модуль.
20. Множества
27
Константы множественного типа записываются в виде заключенной в
квадратные скобки последовательности элементов или интервалов базового типа,
разделенных запятыми, например:
['A', 'C']
[0, 2, 7]
[3, 7, 11..14]
Константа вида [ ] означает пустое подмножество.
21. Записи
28
Инициализация записей осуществляется с помощью типизированных констант:
type RecType= Record
x,y: Word;
dim: Array[1..3] of Byte
end;
const Rec: RecType= (x: 127; y: 255; dim: (2, 4, 8));
22. Файлы
29
Пример описания файлов компонентного типа:
var f1: File of Real;
f2: File of Integer;
Бестиповые файлы описываются с помощью служебного слова File:
var f: File;
Файловые переменные, которые описаны в программе, называют логическими
файлами. Подпрограммы, обеспечивающие ввод-вывод данных, работают с
логическими файлами. Физический файл должен быть связан с логическим до
выполнения процедур открытия файлов.
Турбо Паскаль вводит ряд процедур и функций, применимых для любых типов
файлов: Assign, Reset, Rewrite, Close, Rename, Erase, Eof, IOResult.
Процедура Assign(var f; FileName: String) связывает логический файл f с
физическим файлом, полное имя которого задано в строке FileName.
Процедура Reset(var f) открывает логический файл f для последующего чтения
данных (открывает входной файл).
Процедура Rewrite(var f) открывает логический файл f для последующей записи
данных (открывает выходной файл).
Процедура Close(var f) закрывает логический файл. Вызов процедуры Close
необходим при завершении работы с выходным файлом. Если по какой-то причине
процедура Close не будет выполнена, файл все-же будет создан на внешнем
устройстве, но содержимое последнего буфера в него не будет перенесено. Для
входных файлов использование оператора Close необязательно.
Логическая функция EOF(var f): Boolean возвращает значение TRUE, когда при
чтении достигнут конец файла.
Процедура Rename(var f; NewName: String) позволяет переименовать
физический файл, связанный с логическим файлом f. Переименование возможно
после закрытия файла.
Процедура Erase(var f) уничтожает физический файл, который был связан с
переменной f. Файл к моменту вызова процедуры Erase должен быть закрыт.
Функция IOResult: Integer возвращает целое число, соответствующее коду
последней ошибки ввода-вывода. При нормальном завершении операции функция
вернет значение 0.
Особое место в языке Паскаль занимают текстовые файлы, компоненты
которых имеют символьный тип. Для описания текстовых файлов используется тип
Тext:
var TF1, TF2: Text;
Текстовые файлы представляют собой последовательность строк, а строки –
последовательность символов. Строки имеют переменную длину, каждая строка
завершается признаком конца строки.
С признаком конца строки связана функция EOLn(var T: Text): Boolean, где Т –
имя текстового файла. Эта функция принимает значение TRUE, если достигнут
конец строки, и значение FALSE, если конец строки не достигнут.
Для операций над текстовыми файлами определен также оператор ReadLn(T),
который пропускает строку до начала следующей, и WriteLn(T), который завершает
строку файла признаком конца строки.
30
Для работы с текстовыми файлами введена расширенная форма операторов
ввода и вывода. Оператор Read(T, X1, X2, ... XK) эквивалентен группе операторов
begin
Read(T,X1);
...........
Read(T,XK)
end;
Здесь Т – текстовый файл, а переменные Х1, Х2, … ХК могут быть
переменными целого, действительного или символьного типа, или строкой.
Оператор Write(T, X1, X2, ... XK) эквивалентен группе операторов
begin
Write(T,X1);
...........
Write(T,XK)
end;
Здесь Т – также текстовый файл, но переменные Х1, Х2, … ХК могут быть
целого, действительного, символьного, логического типа или строкой.
К текстовым файлам относятся стандартные файлы INPUT и OUTPUT, работа с
которыми имеет следующие особенности:
- имена этих файлов в списках ввода-вывода не указываются;
- применение процедур Reset, Rewrite и Close запрещено;
- для работы с INPUT и OUTPUT используется функция EOLn без параметров.
Пример программы, которая вводит строку с клавиатуры и сохраняет в
текстовом файле с именем text.txt:
PROGRAM SAVESTR;
var s: string;
outf: Text; { тип файла – текстовый }
begin
{ Открыть файл для записи данных }
assign(outf,'text.txt');
rewrite(outf);
writeln('Введите строку:'); readln(s);
writeln(outf,s); { запись строки в файл }
close(outf) { закрытие файла }
end.
Турбо Паскаль вводит дополнительные процедуры и функции, применимые
только к текстовым файлам: SetTextBuf, Append, Flush, SeekEOLn, SeekEOF.
Процедура SetTextBuf(var f: Text; var Buf; BufSize: Word) служит для
увеличения или уменьшения буфера ввода-вывода файла f (размер буфера по
умолчанию 128 байт). Увеличение размера буфера сокращает количество
обращений к диску. Рекомендуется изменять размер буфера до открытия файла.
Буфер начнется с первого байта переменной Buf. Размер буфера задается в
необязательном параметре BufSize, а если этот параметр отсутствует, размер буфера
определяется длиной переменной Buf.
Процедура Append(var f: Text) открывает уже существующий файл для
дозаписи в конец файла.
31
Процедура Flush(var f: Text) принудительно записывает данные из буфера в
файл.
Функция SeekEOLn(var f: Text): Boolean возвращает значение True, если до
конца строки остались только пробелы.
Функция SeekEOF(var f: Text): Boolean возвращает значение True, если до
конца файла остались строки, заполненные пробелами.
Компонентный файл – это файл с объявленным типом его компонент.
Компонентные файлы состоят из машинных представлений значений переменных,
они хранят данные в том же виде, что и память ЭВМ.
Описание величин файлового типа имеет вид:
type M= File Of T;
где М – имя файлового типа, Т – тип компоненты. Например:
type FIO= String[20];
SPISOK=File of FIO;
var STUD, PREP: SPISOK;
Здесь STUD, PREP – имена файлов, компонентами которых являются строки.
Описание файлов можно задавать в разделе описания переменных:
var fsimv: File of Char;
fr: File of Real;
Компонентами файла могут быть все скалярные типы, а из структурированных
– массивы, множества, записи.
Операции над компонентными файлами производятся с помощью процедур
Reset, Rewrite, Read, Write и Close.
Для ввода-вывода используются процедуры Read(f, X) и Write(f, X), где f – имя
логического файла, Х – переменная, массив, строка, множество или запись.
Процедура Read(f, X) читает одну компоненту файла и записывает ее в X..
Процедура Write(f, X) записывает X в файл как одну из компонент.
Для работы с компонентными файлами введена расширенная форма операторов
ввода и вывода Read(f, X1, X2 … XK) и Write(f, X1, X2, … XK), где f – файл, а
переменные Х1, Х2, … ХК должны иметь тип компонент файла f.
Рассмотрим в качестве примера программу, которая заполняет случайными
значениями в диапазоне от 0 до 1 массив из 100 элементов типа Real и сохраняет его
в виде двоичного файла numbers.bin:
PROGRAM RNDNUM;
var i: integer;
a: array[1..100] of real;
outf: File of real;
begin
randomize;
assign(outf,'numbers.bin');
rewrite(outf);
for i:=1 to 100 do begin a[i]:=random; write(outf,a[i]); end;
close(outf)
end.
32
Бестиповые файлы позволяют записывать на диск произвольные участки
памяти ЭВМ и считывать их с диска в память. Операции обмена с бестиповыми
файлами осуществляется с помощью процедур BlokRead и BlockWrite. Кроме того,
вводится расширенная форма процедур Reset и Rewrite.
Перед использованием логический файл
var f: File;
должен быть связан с физическим с помощью процедуры Assign. Далее файл
должен быть открыт для чтения или для записи процедурой Reset или Rewrite, а
после окончания работы закрыт процедурой Close.
При открытии файла длина буфера устанавливается по умолчанию в 128 байт.
Турбо Паскаль позволяет изменить размер буфера, для чего следует открывать файл
расширенной записью процедур
Reset(var f: File; BufSize: Word)
или
Rewrite(var f: File; BufSize: Word)
Параметр BufSize задает число байтов, считываемых из файла или
записываемых в него за одно обращение (минимальное значение – 1 байт,
максимальное – 64К байт).
Чтение данных осуществляется процедурой
BlockRead(var f: File; var X; Count: Word; var QuantBlock: Word);
Эта процедура читает в переменную X блоки данных, количество которых
заданно параметром Count. Размер блока равен размеру буфера. За одно обращение
нельзя прочесть больше, чем 64К байт.
Необязательный параметр QuantBlock возвращает число блоков, прочитанных
текущей операцией BlockRead. В случае успешного завершения операции чтения
QuantBlock = Count, в случае аварийной ситуации параметр QuantBlock будет
содержать число успешно прочитанных блоков.
Запись данных выполняется процедурой
BlockWrite(var f: File; var X; Count: Word; var QuantBlock:Word);
которая записывает из переменной X в файл количество блоков, заданное
параметром Count. Размер блока равен размеру буфера.
Необязательный параметр QuantBlock возвращает число блоков, записанных
текущей операцией BlockWrite.
33
Рассмотренные ранее средства работы с файлами обеспечивают
последовательный доступ.
Турбо Паскаль позволяет применять способ прямого доступа к компонентным
и бестиповым файлам,. Прямой доступ означает возможность заранее определить в
файле блок, к которому будет применена операция ввода-вывода. В случае
бестиповых файлов блок равен размеру буфера, для компонентных файлов блок –
это одна компонента файла.
Прямой доступ предполагает, что файл представляет собой последовательность
блоков. Если файл содержит n блоков, то они нумеруются от 1 до n. Кроме того,
вводится понятие условной границы между блоками, при этом условная граница с
номером 0 расположена перед блоком с номером 1, граница с номером 1
расположена перед блоком с номером 2 и, наконец, условная граница с номером n
находится после блока с номером n.
Реализация прямого доступа осуществляется с помощью подпрограмм FileSize,
FilePos, Seek и Truncate.
Функция FileSize(var f): Longint возвращает количество блоков в файле f.
Функция FilePos(var f): Longint возвращает текущую позицию в файле f.
Позиция в файле – это номер условной границы. Для только что открытого файла
текущей позицией будет граница с номером 0. Это значит, что можно записать или
прочесть блок с номером 1. После чтения или записи первого блока текущая
позиция переместится на границу с номером 1, и можно будет обращаться к блоку с
номером 2. После прочтения последней записи значение FilePos равно значению
FileSize.
Процедура Seek(var f; N: Longint) устанавливает текущую позицию в файле. В
параметре N должен быть задан номер условной границы, предшествующей блоку, к
которому будет производиться обращение.
Процедура Truncate(var f) устанавливает в текущей позиции признак конца
файла и удаляет все последующие блоки.
Рассмотрим в качестве примера программу, которая выводит на экран размер
указанного пользователем файла в байтах:
PROGRAM FSIZE;
var s: string;
inf: file of byte;
begin
write('Введите имя файла: '); readln(s);
assign(inf,s);
reset(inf);
writeln('Размер файла ',filesize(inf),' байт');
close(inf);
readln
end.
34
24. Указатели
35
Динамическая переменная не указывается явно в описаниях переменных и к
ней нельзя обратиться по имени. Доступ к таким переменным осуществляется с
помощью указателей и ссылок.
Работа с динамической областью памяти реализуется с помощью процедур и
функций New, Dispose, GetMem, FreeMem, Mark, Release, MaxAvail, MemAvail,
SizeOf.
Процедура New(var p: Pointer) выделяет место в динамической области памяти
для размещения динамической переменной p^ и ее адрес присваивает указателю p.
Процедура Dispose(var p: Pointer) освобождает участок памяти, выделенный для
размещения динамической переменной процедурой New, и значение указателя p
становится неопределенным.
Проуедура GetMem(var p: Pointer; size: Word) выделяет участок памяти в heap-
области и присваивает адрес его начала указателю p. Размер участка в байтах
задается параметром size.
Процедура FreeMem(var p: Pointer; size: Word) освобождает участок памяти,
адрес начала которого определен указателем p, а размер – параметром size. Значение
указателя p становится неопределенным.
Процедура Mark(var p: Pointer) записывает в указатель p адрес начала участка
свободной динамической памяти на момент ее вызова.
Процедура Release(var p: Pointer) освобождает участок динамической памяти,
начиная с адреса, записанного в указатель p процедурой Mark, т.е. очищает память,
которая была занята после вызова процедуры Mark.
Функция MaxAvail: Longint возвращает длину в байтах самого длинного
свободного участка динамической памяти.
Функция MemAvail: Longint полный объем свободной динамической памяти в
байтах.
Вспомогательная функция SizeOf(X): Word возвращает объем в байтах,
занимаемый X, причем X может быть именем переменной или именем типа.
Рассмотрим некоторые примеры работы с указателями.
var p1, p2: ^Integer;
Здесь p1 и p2 – указатели или переменные ссылочного типа.
p1:=NIL; p2:=NIL;
После выполнения этих операторов присваивания указатели p1 и p2 не будут
ссылаться ни на какой конкретный объект.
New(p1); New(p2);
Процедура New(p1) выполняет следующие действия:
- в памяти ЭВМ выделяется участок для размещения величины целого типа;
- адрес этого участка присваивается переменной p1.
Аналогично, процедура New(p2) обеспечит выделение участка памяти, адрес
которого будет записан в p2.
После выполнения операторов присваивания
p1^:=2; p2^:=4;
в выделенные участки памяти будут записаны значения 2 и 4 соответственно:
В результате выполнения оператора присваивания
p1^:=p2^;
36
в участок памяти, на который ссылается указатель p1, будет записано значение 4.
После выполнения оператора присваивания
p2:=p1;
оба указателя будут содержать адрес первого участка памяти:
Переменные p1^, p2^ являются динамическими, так как память для них
выделяется в процессе выполнения программы с помощью процедуры New.
Динамические переменные могут входить в состав выражений, например:
p1^:=p1^+8;
Пример. В результате выполнения программы:
Program DemoPointer;
var p1,p2,p3:^Integer;
begin
p1:=NIL; p2:=NIL; p3:=NIL;
New(p1); New(p2); New(p3);
p1^:=2; p2^:=4;
p3^:=p1^+Sqr(p2^);
writeln('p1^=',p1^,' p2^=',p2^,' p3^=',p3^);
end.
на экран дисплея будут выведены результаты:
p1^=2 p2^=4 p3^=18
37
InitGraph(var GrDriver, GrMode: Integer; Path: String) – инициализирует графический
режим.
Line(X1,Y1,X2,Y2: Integer) – рисует прямую линию от точки (X1,Y1) до точки
(X2,Y2).
LineRel(dX,dY: Integer) – рисует прямую линию от текущей точки (X,Y) до точки
(X+dX,Y+dY).
LineTo(X,Y: Integer) – рисует прямую линию от текущей точки до точки (X,Y).
MoveRel(dX,dY: Integer) – передвигает указатель из текущей точки (X,Y) в точку
(X+dX,Y+dY).
MoveTo(X,Y: Integer) – передвигает указатель из текущей точки в точку (X,Y).
OutText(TextString: String) – выводит текст на экран, начиная с того места, где
находится указатель.
OutTextXY(X,Y: Integer; TextString: String) – выводит текст на экран, начиная с
точки с координатами (X,Y).
PutPixel(X,Y: Integer; ColorPixel: Word) – выводит точку с координатами (X,Y) и
цветом ColorPixel.
Rectangle(X1,Y1,X2,Y2: Integer) – рисует рамку с координатами противоположных
вершин (X1,Y1) и (X2,Y2).
SetBkColor(Color: Word) – устанавливает цвет фона.
SetColor(Color: Word) – устанавливает основной цвет для рисования.
SetFillPattern(Pattern: FillPatternType; Color: Word) – устанавливает определѐнный
пользователем шаблон заполнения.
SetFillStyle(Pattern, Color: Word) – устанавливает тип и цвет заполнения.
SetLineStyle(LineStyle, Pattern, Thickness: Word) – устанавливает стиль, образец и
толщину линий.
Часто используются также следующие функции:
GetX: Integer – возвращает текущую координату X.
GetY: Integer – возвращает текущую координату Y.
GetMaxX: Integer – возвращает максимальную горизонтальную координату X.
GetMaxY: Integer – возвращает максимальную вертикальную координату Y.
Для работы с графикой программы на Турбо Паскале используют так
называемые bgi-драйверы: требуется или скопировать bgi-драйвер в рабочий
каталог, или указать путь к bgi-драйверу.
Все современные AT-совместимые компьютеры используют драйвер
egavga.bgi, который работает в режиме 640480 точек, 16 цветов. Соответственно,
код цвета рисования может принимать значения от 0 до 15. Начало системы
координат расположено в левом верхнем углу экрана, координатная ось Y
направлена сверху вниз.
Рассмотрим в качестве примера программу, которая включает графический
режим, на рисует красную точку в центре экрана, выбирает желтый цвет для
рисования, рисует отрезок, прямоугольник, треугольник, окружность, эллипс и
строку текста, ожидает нажатия клавиши Enter, а затем восстанавливает текстовый
режим и завершает работу:
38
PROGRAM GTEST;
uses Graph;
var Drv,Mode: integer;
Triangle: array[1..4] of PointType;
begin
Drv:= Detect;
InitGraph(Drv, Mode, '');
PutPixel(320,240,12); {Нарисовать красную точку в центре
экрана}
SetColor(14); {Установить желтый цвет рисования}
Line(10,5,80,30); {Нарисовать линию}
Rectangle(5,35,70,60); {Нарисовать прямоугольник}
{Задать координаты вершин треугольника}
Triangle[1].X:=40; Triangle[1].Y:=70;
Triangle[2].X:=20; Triangle[2].Y:=90;
Triangle[3].X:=60; Triangle[3].Y:=90;
Triangle[4].X:=40; Triangle[4].Y:=70;
DrawPoly(4, Triangle); {Нарисовать треугольник}
Circle(50,120,20); {Нарисовать окружность}
Ellipse(50,160,0,360,30,15); {Нарисовать эллипс}
OutTextXY(15,200,’Hello!’); {Вывести текст}
readln;
CloseGraph
end.
Тип PointType предназначен для хранения координат точки. Описание:
type PointType = Record
X. Y: Integer;
end;
Для процедуры FillPoly необходимо указать координаты вершин
многоугольника в виде массива точек, а процедура DrawPoly требует на одну точку
больше – координаты начальной и конечной точек должны совпадать. Дело в том,
что процедура DrawPoly используется не только для рисования многоугольников, но
и для вывода ломанных линий.
Тип FillPatternType определяет образец закраски. Описание:
FillPatternType = array[1..8] of Byte;
Приведем в качестве примера программу, которая закрашивает треугольник в
красный цвет:
PROGRAM REDT;
uses Graph;
{Использовать шаблон для сплошной заливки}
const Pat: FillPatternType = ($FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF);
var Drv,Mode: integer;
Triangle: array[1..3] of PointType;
begin
Drv:= Detect;
InitGraph(Drv, Mode, '');
SetColor(12);
SetFillPattern(Pat, 12);
39
Triangle[1].X:=200; Triangle[1].Y:=100;
Triangle[2].X:=100; Triangle[2].Y:=200;
Triangle[3].X:=300; Triangle[3].Y:=200;
FillPoly(3, Triangle);
readln;
CloseGraph
end.
40
Учебное издание
СОСТАВИТЕЛЬ:
КУЛАКОВ ВЛАДИМИР ГЕННАДЬЕВИЧ
Редактор
Технический редактор
http://www.miem.edu.ru/rio
rio@miem.edu.ru
41