Программирование на Паскале Кемерово, 2005 2 Содержание 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. Основы алгоритмов. Блок-схемы. ........................................................................................4 Среда Turbo Pascal .................................................................................................................5 Структура программы ...........................................................................................................6 Основные типы данных ........................................................................................................7 Операторы ввода и вывода ...................................................................................................7 Оператор присваивания. Стандартные функции. ...............................................................8 Оператор условного и безусловного перехода. ................................................................10 Оператор выбора .................................................................................................................11 Операторы цикла .................................................................................................................12 Массивы............................................................................................................................14 Текстовый файл ...............................................................................................................16 Записи ...............................................................................................................................19 Процедуры и функции пользователя .............................................................................24 Модуль CRT .....................................................................................................................29 Модуль Graph ...................................................................................................................32 3 1. Основы алгоритмов. Блок-схемы. Алгоритм – это последовательный набор команд, направленный на решение той или иной задачи. Алгоритм можно оформить в виде: - в виде списка последовательных команд, - алгоритмического языка, - блок-схемы, - программы на языке программирования. Алгоритмы могут быть: 1) линейные 2) разветвляющиеся 3) циклические 4) составные Блок Назначение Команда на Паскале Начало действия Begin Начало команд Блок ввода данных Read( ); Readln( ); Блок действия A:=5; B:=A+6; Блок выбора решения If A>0 then {да}… else {нет}… Блок для формирования цикла For k:=1 to 10 do … For k:=10 downto 1 do … Ввод данных Выполнение действия нет условие Параметры цикла да Тело цикла 4 Блок Вывод результата Назначение Блок вывода данных на печать, в файл или на экран Команда на Паскале Write (); Writeln (); Блок завершения алгоритма End. Конец 2. Среда Turbo Pascal Для выполнения программы на языке программирования Паскаль используют следующие этапы: - с помощью текстового редактора набирают код программы - с помощью программы - компилятора создают выполняемый код программы - с помощью программы – отладчика проводят пошаговую реализацию программы и контроль данных. Все это выполняется с помощью специальной программы – интегрированной среды Turbo Pascal: Интегрированная среда Turbo Pascal работает как многооконный редактор и как отладчик программ. Команда Назначение F10 Выход в меню File, New Создание нового окна для редактирования File, Save F2 Сохранение в файл File, Open… F3 Открыть файл в текущее окно File, Save as… Сохранить под именем файла File, Change dir… Изменить текущий каталог File, Exit Alt+X Выход Option, Directories… Задание путей к файлам Turbo Pascal Edit, Undo Alt+BackSpace Отмена предыдущего действия Edit, Cut Shift+Delete Вырезать выделенный фрагмент Edit, Copy Ctrl+Insert Скопировать выделенный фрагмент Edit, Paste Shift+Insert Вставить выделенный фрагмент Edit, Clear Ctrl+Delete Очистить выделенный фрагмент Window, Close Alt+F3 Закрыть активное окно 5 Команда Window, Zoom F5 Window, Size/Move Ctrl+F5 Window, Next Window, Previous Window, List … Help, Index F6 Shift+F6 Alt+0 Shift+F1 Help, Topic search Compile, Compile Run, Run Ctrl+F1 Alt+F9 Ctrl+F9 Run, Step over Run, Trace into F8 F7 Run, Go to cursor F4 Run, Program reset Debug, User screen Ctrl+F2 Ctrl+Break Alt+F5 Debug, Add watch… Ctrl+F7 Breakpoints On/Off Go to window Ctrl+F8 Alt+№ окна Назначение Развернуть окно Перейти в режим изменения размера и местоположения текущего окна Перейти к следующему окну Перейти к предыдущему окну Перейти к списку открытых окон Вывод окна помощи в виде предметного указателя Вывод окна помощи по текущему слову Компилировать Выполнить или продолжить выполнение программы Выполнить один шаг программы Выполнить один шаг программы с заходом в процедуры и функции пользователя Выполнить программу до местоположения курсора Остановить выполнение программы Прервать выполнение программы Перейти на экран пользователя для просмотра результата выполнения программы Добавить в окно Watch контрольные значения Добавить/отключить точки останова Перейти в окно под заданным № 3. Структура программы Программа на Паскале строится по логике построения предложения. Каждое действие заканчивается точкой с запятой, а сама программа – точкой. В Паскале существует понятие идентификатора – уникальное имя, которое дается определенному понятию для того, чтобы можно было к нему обратиться во время выполнения программы. Это – имя программы, имена используемых модулей, имена констант, переменных и типов, имена функций и процедур, определенных пользователем. {блок описаний используемых идентификаторов} Назовем программу …; Program name; которая использует процедуры, функции, константы, типы следующих модулей (библиотек) …; определим метки, используемые в программе в операторе GOTO, …; зададим константы …; Uses Crt; Label 1; Const myname=’Rita’; будем использовать свои имена для описания типов переменных и констант …; в программе используем переменные …, такие, что они имеют тип …; Type myint=Integer; Var A,b: myint; 6 будем использовать функцию …, которая возвращает значение указанного типа и выполняет следующие действия …; Procedure используем свою процедуру …, myProc(<список параметров>); которая выполняет с введенными {описание работы процедуры} параметрами следующие действия …; {основной блок выполнения программы} Begin а теперь начнем выполнять {блок операторов} следующие команды, записанные в End. виде известных операторов языка Паскаль … до окончания алгоритма. Function myFunc(<список параметров>):<тип>; {описание работы функции} 4. Основные типы данных Тип Integer Real Значение Натуральные или целые числа Вещественные числа с десятичной запятой String Строка символов, заключенных в апострофы Char Один символ, заключенный в апострофы Boolean Логическое значение TRUE (истина) или FALSE (ложь) Примеры -2, 10, 0, 565 -2.5, 100.0005, 1.14587е+20 (1.14587*1020), 2.0047е-05 (2.0047*10-5) ’12,45’, ‘Мир, труд, май!’, ‘For English city.’, ‘1000000000000001’ ‘F’, ‘Ф’, ’1’, ’0’, ’*’ True, False 5. Операторы ввода и вывода Для работы с данными их нужно задать – ввести с клавиатуры. READ(); {считываем данные в строке в переменные, указанные в скобках} READLN(); {считываем данные в строке в переменные, указанные в скобках, и переходим на следующую строку} В скобках необходимо указать имена переменных, которым будут присвоены введенные значения. READ(N,M); {ввести два целых числа через пробел (N, M объявлены как переменные целого типа)} READLN(F); READLN(S); {ввести вещественное число через точку (F должно быть объявлено как переменная вещественного типа), затем с новой строки ввести строку символов размером не более 255 (S должно быть объявлено как строковая переменная) } ВНИМАНИЕ: 1) если ввести число и строку в одну строчку, то строка не будет считана. 2) если ввести сначала строку, а потом число оператором READLN(S,F); то число может быть так и не прочитано, так как в строку может войти и введенное число. Тогда нужно ограничить число вводимых символов в строке (например, String[5]) и вводить только такое количество символов, а затем вещественное число. Для вывода результата используют оператор WRITE(); {написать (вывести на печатающее устройство) данные в строку} 7 WRITELN(); {написать (вывести на печатающее устройство) данные в строку и перейти на следующую строку} В скобках через запятую перечисляют переменные и константы с указанием формата (шаблона) для вывода: WRITE(‘ N=’, N:4); {вывести ‘ N=’, затем значение переменной N, такое, что оно разместилось бы в 4 позиции} WRITELN(‘ F=’, 3.456:7:2); {вывести ‘ F=’, затем число 3.456 так, чтобы оно разместилось бы в 7 позиций, из которых 2 отведено под десятичные цифры} WRITE(‘Введите s =’:20); READLN(S); {вывести ‘ Введите s =’ так, чтобы оно разместилось в 20 позиций, затем ввести с клавиатуры значение, которое будет присвоено переменной S} 6. Оператор присваивания. Стандартные функции. Можно задать значения через оператор присваивания. <Имя переменной> := <выражение или значение>; {пусть заданная переменная получит (присвоит) такое значение, которое будет равно заданному выражению} Выражения в зависимости от типа переменной могут быть - логическими, арифметическими или строковыми. Выражение строится по определенным правилам с использованием применимых к этому типу операций: Выражения 5<3 5>3 5<>3 5=3 5<=3 5>=3 Not (5<>3) (5>3)and(5>10) (5<3)or(5>10) 5 xor 5 xor 3 5 in [1..5] 5+5 5-5 5*5 5/2 5 div 2 5 mod 2 ‘Я‘+’ и ‘ +’мы’ Полученное значения Операции Логические False < True > True <> False = False <= True >= False Not True And True Or 3 Xor True In Арифметические 10 + 0 25 * 2.5 / 2 Div 1 Mod Строковые ‘Я и мы’ + 8 Назначение Меньше Больше Не равно Равно Меньше или равно Больше или равно Не (отрицание) И (пересечение) Или (объединение) Исключающее или Принадлежит множеству Сложение Вычитание Умножение Десятичное деление Целочисленное деление Остаток от деления Конкатенация (соединение) Кроме перечисленных операций над переменными могут воздействовать стандартные функции или функции, определенные пользователем. Функция Ord(‘Z’) Chr(90) Odd(6) Параметры Символ Число Целое число Pred(34) Целое число или символ Succ(33) Целое число или символ Abs(-5) Sqr(2) Sqrt(4) Sin(pi) Cos(pi/2) Arctan(0) Int(3.45) Frac(3.45) Round(3.45) Trunc(2.5) Число Число Число Число Число Число Вещественное число Вещественное число Вещественное число Вещественное число Random(2) Целое число N Random Без параметров Concat(‘Я’, ’ и ’, ’мы’) Length(‘мама’) Pos(‘м’,’мама’) Copy(’мама’, 1, 2) Список строковых переменных или значений Строка Подстрока и строка Строка, номер позиции и количество символов Значение Код символа (=90) Символ, указанного кода (=’Z’) Логическое значение истина, если число нечетное, или ложь, если число четное (=False) Предыдущий символ или число (=33) Следующий символ или число (=34) Абсолютное значение числа (=5) Квадрат числа (=4) Корень квадратный из числа (=2) Синус числа (=0) Косинус числа (=0) Арктангенс числа (=0) Целая часть (=3) Дробная часть (=0.45) Целое число без округления (=3) Целое число округленное до ближайшего целого числа (=3) Случайным образом полученное целое число из диапазона [0..N-1] (=0 или =1) Вещественное число из диапазона от 0 до 1 Строка, объединяющая эти значения (=’Я и мы’) Длина строки (=4) Позиция подстроки в строке (=1) Копирует из строки, начиная с указанной позиции, заданное число символов (=‘ма’) Кроме функций определены следующие стандартные процедуры: Процедура Inc (I) Параметры Переменная целого типа и шаг изменения Dec (I,10) Переменная целого типа и шаг изменения Insert (‘!’, S, 1) Подстрока, строка, номер позиции Delete (S, 1, 2) Строка, номер позиции и количество символов 9 Назначение Та же переменная, увеличенное на указанное число шагов или на 1, если не указан шаг (i:=i+1) Та же переменная, уменьшенное на указанное число шагов или на 1, если не указан шаг (i:=i-10) Вставляет подстроку в строку, начиная с указанной позиции (S:=‘!’+S) Удаляет из строки, начиная с указанной позиции, заданное Str (5.6, S) Val (‘5.6’, K, code) Число и строковая переменная Строка, числовая константа и код ошибки число символов (если S:=’Ok!’, то после delete S:=’!’) Преобразует число в строку (S:=’5.6’;) Преобразует строку в число (K:=5.6; code:=0;) 7. Оператор условного и безусловного перехода. Рассмотрим операторы, реализующие разветвленный алгоритм (алгоритм с двумя выборами пути решения, к которому переходим в зависимости от значения логического выражения или ответа на вопрос). Фрагмент блок-схемы Оператор Назначение да Условие, вопрос Если <условие> истинно, то выполняем действия 1, иначе (если условие ложно) выполняем действие 2 IF <УСЛОВИЕ> THEN <ОПЕРАТОРЫ 1>; <ОПЕРАТОРЫ 2>; Если <условие> истинно, то выполняем действия 1, и потом ( и если условие ложно) выполняем действие 2 2. Действия, если ответили нет 1. Действия, если ответили да да нет IF <УСЛОВИЕ> THEN <ОПЕРАТОРЫ 1> ELSE <ОПЕРАТОРЫ 2>; Условие, вопрос нет 1. Действия, если ответили да 2. Действия, если ответили нет и после действия да Задание №1. Необходимо найти значение функции y=1/x. При x=0 функция не имеет значения. Тогда мы будем выдавать это сообщение. Построим блок-схему к задаче и напишем программу на Паскале. Построим алгоритм решения задачи: 1. Определим тип переменной х как вещественный. 2. Введем значение переменной х с клавиатуры. 3. Проверим условие на 0. Если оно истинно, то выводим сообщение, иначе выводим значение функции. 4. В операторе вывода добавим формат для вещественных чисел. Приведем блок-схему и по ней построим программу действий: 10 Описание переменных: Var x:real; Begin начало Write(‘Введите значение х=’); {комментарий} Readln(x); Ввод х да нет Х=0 ? Вывод сообщения Вывод Y=1/X IF (x=0) Then {да} Else {нет} writeln(‘Y=’, writeln(‘Функция не 1/X:9:4); имеет значения!’) End. конец 8. Оператор выбора Создадим меню команд: «Ввод данных», «Вывод результата», «Выход». По выбранному пункту будем выводить соответствующие команды. Описание метки: Label Menu; {задаем метку строки для перехода на нее} Описание переменных: Var K:[0..2]; {указываем диапазон изменения данных} x: real; Begin начало Menu: {Выводим пункты меню} Writeln(‘0. Выход’); Выбор команды меню Writeln(‘1. Ввод данных’); Writeln(‘2. Вывод результатов’); Write(‘Сделай выбор ->’);readln(k); да нет K=0? {Сделаем выбор по значению переменной k} Case k of да нет {если k=0}0: exit; K=1 {если k=1}1: begin Write(‘Введите значение х=’); readln(x); end; Ввод x нет K=2 {если k=2}2: да да X=0? Вывод сообщения нет Вывод y=1/x конец If x=0 Then writeln( Else writeln( ‘Функция не имеет ‘Y=’,1/X:9:4); значения!’) End; {конец выбора} Goto Menu; {переходим на строку, помеченной как Menu} End. 11 9. Операторы цикла Рассмотрим операторы циклического алгоритма (алгоритм, который позволяет выполнять одно и то же действие несколько раз, или до тех пор, пока не выполниться условие останова): Фрагмент блок-схемы Оператор Назначение FOR <ПАРАМЕТР> := <НАЧАЛЬНОЕ ЗНАЧЕНИЕ> TO/DOWNTO <КОНЕЧНОЕ ЗНАЧЕНИЕ> DO <ОПЕРАТОРЫ ЦИКЛА>; Изменение параметра цикла {Цикл с параметром} Тело цикла WHILE <УСЛОВИЕ> DO <ОПЕРАТОРЫ ЦИКЛА>; {Цикл с предусловием} Условие продолжения цикла Нет Для каждого значения переменной, равной сначала начальному значению, до конечного значения с шагом 1 (или -1 при downto) проделать действия в теле цикла. Пока условие истинно, выполняем действия в теле цикла, иначе выходим из цикла. Тело цикла REPEAT <ОПЕРАТОРЫ ЦИКЛА> UNTIL <УСЛОВИЕ>; Тело цикла {Цикл с постусловием} Условие окончания цикла Нет 12 Повторять действия в теле цикла до тех пор, пока условие не будет выполнено. Задание №1. Будем повторять вычисления функции y=1/x до тех пор, пока не введем х=0. {цикл с пост-условием} Описание переменных начало Ввод Х Вывод 1/x Да Х=0 конец Var x:real; Begin Repeat {повторяем} Write(‘Введите значение х=’);readln(x); If x=0 Else Then writeln writeln (‘Y=’,1/X:9:4); (‘Функция не имеет значения!’) Until x=0;{пока х не станет равен 0} End. {цикл с предусловием} Описание переменных начало Var x:real; Begin Write(‘Введите значение х=’); readln(x); Ввод Х Нет Х≠0? While x<>0 do повторяем} {пока х не равен 0 Begin Да writeln(‘Y=’,1/X:9:4); Write(‘Введите значение х=’);readln(x); Вывод 1/x Ввод Х конец End; End. 13 Задание №2. Вычислим значение функции y=1/x с начальным значением х и с каждым изменением его на заданный шаг step ровно заданное N количество раз . {цикл с параметром} Описание переменных начало Ввод a, step, N I = 1, N; 1 X = A + (I -1)*STEP Да X = 0? Нет Вывод x, 1/x конец Var X, step :real; I, N :integer; Begin Write(‘Введите начальное значение х=’);readln(а); Write(‘Введите шаг изменения х=’);readln(step); Write(‘Введите количество вычислений у=1/х’);readln(N); For i:=1 to N do {для переменной i, начиная с 1 до N делаем} Begin X := a+(i-1)*step; If x=0 Then writeln(‘Функция не имеет значения!’) Else writeln(‘Y=’,1/X:9:4); End; End. 10.Массивы Массив – это специальный тип для хранения данных в виде определенной структуры – ряда (array) чисел, последовательности переменных с индексами, упорядоченной последовательности символов. Таким образом, для массива характерно наличие нумерованных элементов. Например, последовательность чисел ряда a1, a2, …, a10 можно представить как вектор целых чисел размерности 10 и объявить для этого 10 переменных Var a1, a2, a3, a4, a5, a6, a7, a8, a9, a10: integer; Но доступ к таким переменным придется организовывать в виде линейного алгоритма. 10 раз вводить, 10 раз изменять в операторе присваивании, 10 раз выводить, и т.д. А если вектор возрастет до 20 чисел? Сколько раз придется добавлять таких операторов? I. Одномерный массив В этом случае, хотелось бы индексы (номера) отделить от самого названия и вынести их в отдельную структуру. Это возможно сделать, записав тип массив следующим образом: Type Vector=array[1..10] of integer; Теперь, объявив переменную этого типа как Var а: vector; мы сможем обратиться к каждому элементу непосредственно, указав лишь в квадратных скобках номер этого элемента: a[1] – это 1-ый элемент, a[2] – это 2-ый элемент, и т.д. Теперь можно обратиться к индексу как к отдельной константе, переменной и включить оператор ввода каждого i-го элемента в цикл по параметру i, изменяющийся от 1 до 10. Таким образом, для ввода 10 или 20 нумерованных чисел потребуется изменить только 14 конечный параметр в цикле по i и алгоритм будет работать без добавления лишних операторов ввода: {ввод сроки чисел} {вывод чисел в строку} for i:=1 to 10 do for i:=1 to 10 do read(a[i]); write(‘ ‘,a[i]); {ввод чисел по-элементно} {вывод чисел в столбец} for i:=1 to 10 do for i:=1 to 10 do readln(a[i]); writeln(a[i]); Задание №1. Занести в массив аргумент и значение функции. Найти наибольшее и наименьшее значение функции. Type Vector=array [1..100] of real; Var step :real; I, N :integer; X,Y :vector; Max, Min:integer; Begin Write(‘Введите начальное значение х=’);readln(а); Write(‘Введите шаг изменения х=’);readln(step); Write(‘Введите количество вычислений у=1/х’);readln(N); For i:=1 to N do {для переменной i, начиная с 1 до N делаем} Begin X[i] := a+(i-1)*step; If x[i]=0 Then writeln(‘Функция не имеет значения!’) Else begin y[i]:=1/x[i]; writeln(‘Y=’,y[i]:9:4); end; End; {находим номер максимального элемента} max:=1; For i:=2 to N do If y[i]>y[max] then max:=I; Writeln(‘наименьшее значение функции в точке x=’, x[min]:6:2,’и равно’, y[min]:9:4); {находим номер наименьшего элемента} min:=1; For i:=2 to N do If y[i]<y[min] then min:=I; Writeln(‘наибольшее значение функции в точке x=’, x[max]:6:2,’и равно’, y[max]:9:4); End. II. Строка как одномерный массив символов Массивом можно представить строку символов, к каждому элементу которого можно обращаться по его номеру. Задание №2. Вывести количество нулей в заданном числе. Var Number:string; 15 Kol_zero:integer; i:integer; begin writeln(‘введите целое число: ’);readln(number); kol_zero:=0; for i:=1 to length(number) do If number[i]=’0’ then inc(kol_zero); Writeln(‘количество нулей в числе равно ’,kol_zero); End. III. Двумерный массив (матрица) Элементом массива может быть сам массив. Тогда получим двумерный массив или матрицу чисел, у которого есть индекс строки и индекс столбца. Задание №3. Задана матрица целых чисел размером 5х5. Определить сумму отрицательных элементов матрицы и произведение положительных элементов матрицы.. Type Vector=array[1..5] of integer; {для хранения строк} Matrica=array[1..5]of vector; {для хранения массива строк} Var A:matrica; S,P:integer; I,j:integer; begin writeln(‘введите матрицу чисел: ’); for i:=1 to 5 do begin for j:=1 to 5 do read(a[i][j]);{читаем i-ую строку матрицы} readln;{переходим к следующей строке} end; {сумма отрицательных чисел} S:=0; For i:=1 to 5 do For j:=1 to 5 do {перебираем все элементы} If a[I,j]<0 then S:=S+a[I,j]; Writeln(‘сумма отрицательных чисел =’,S); {произведение положительных чисел} p:=1; For i:=1 to 5 do For j:=1 to 5 do {перебираем все элементы} If a[I,j]>0 then p:=p*a[I,j]; Writeln(‘произведение положительных чисел =’,P); End. 11.Текстовый файл Если массивы или матрицы заданы больших размеров, то их лучше хранить в отдельном файле и считывать элементы из них. I. Текстовые и типизированные файлы. Существует текстовые и типизированные файлы (эти файлы хранят не только данные, но и структуру данных). Для работы с такими данными служит специальные файловые типы: Var 16 F: text; G:file of integer; Они обеспечивают связь непосредственно с самим местоположением данных. Поэтому в начале необходимо это место определить, вызвав процедуру, которая обеспечивает присвоение файловой переменной необходимых данных о местоположении указанного файла assign(<файловая переменная>,<имя файла>); Теперь надо определить, что делать с этим файлом: читать данные, записывать или добавлять в конец, изменять записи в файле, закрыть доступ к файлу. Для этого определены следующие процедуры: для чтения - reset(f); для записи/создания - rewrite(f); для добавления - append(f); для закрытия - close(f); Причем в параметрах указывается уже не сам файл по имени, а его идентификатор файловая переменная. Теперь для ввода или вывода данных в операторе ввода/вывода файловую переменную необходимо писать первым в списке, чтобы указать, откуда читать или куда записывать данные. При этом запись и чтение в текстовый файл и в типизированный файл производится разными способами. Рассмотрим это на примере. Задание №1. Создайте матрицу вещественных чисел размером 10х10, используя функцию генерации случайных чисел. Сохраните матрицу в текстовый файл и в типизированный. Посмотрите различия хранения данных через текстовый редактор и сравните размеры файлов. Type Matrica=array[1..10, 1..10]of real; Var I,j:integer; A:matrica; F:text; G:file of matrica; Begin Randomize;{задание начального отчета для счетчика случайных чисел} For i:=1 to 10 do For j:=1 to 10 do A[I,j]:=random;{вещественные числа от 0 до 1} {устанавливаем связь с текстовым файлом} Assign(f,’matrica1.txt’);rewrite(f); For i:=1 to 10 do begin For j:=1 to 10 do Write(f,a[I,j]:6:4,’ ‘); {помещаем числа в одну строку} Writeln(f);{переходим на новую строку} End; Close(f); {устанавливаем связь с типизированным файлом} Assign(g,’matrica2.txt’);rewrite(g); Write(g,A); {помещаем всю матрицу сразу} Close(g); End. 17 II. Файловые переменные Input и Output. Кроме этого, существуют две файловые переменные, отвечающие за направление ввода и вывода. Input - отвечает за ввод, Output - отвечает за вывод. По умолчанию, эти переменные работают со стандартным вводом с клавиатуры и выводом на экран. Поэтому операторы ввода/вывода Read/Write работают с этими файловыми переменные по умолчанию без явной их записи в списке ввода/вывода. Если эти переменные переопределить на ввод/вывод с файла, то операторы Read/Write будут работать с файлами без явной записи этих переменные в списке ввода/вывода. Задание №2. Создайте матрицу целых чисел размером 3х3, используя функцию генерации случайных чисел. Сохраните матрицу в текстовый файл. Обнулите значения матрицы и вновь введите их с текстового файла. Сравните результаты вывода с результатами в текстовом файле. Const dim=3; {задаем размер матрицы} Type Matrica=array[1..dim, 1..dim]of integer; Var I,j:integer; A:matrica; Begin Randomize;{задание начального отчета для счетчика случайных чисел} For i:=1 to dim do For j:=1 to dim do A[I,j]:=random(101);{целые числа от 0 до 100} {устанавливаем связь с текстовым файлом для вывода} Assign(output,’matrica.txt’);rewrite(output); For i:=1 to dim do begin For j:=1 to dim do Write(a[I,j],’ ‘); {помещаем числа в одну строку} Writeln;{переходим на новую строку} End; Close(output); Fillchar(A,sizeof(A),0);{обнуляем матрицу} {устанавливаем связь с текстовым файлом для ввода данных} Assign(input,’matrica.txt’);rewrite(input); For i:=1 to dim do begin For j:=1 to dim do Read(A[I,j]); Readln; End; Close(input); {выводим матрицу на экран} For i:=1 to dim do begin 18 For j:=1 to dim do Write(a[I,j],’ ‘); {помещаем числа в одну строку} Writeln;{переходим на новую строку} End; Readln;{пауза до нажатия клавиши Enter} End. III. Ввод и вывод в текстовый файл в командной строке. Если файл еще не готов, то можно его задать при выполнении программы из командной строки: C:\myfile.exe <input.txt >output.txt Знак < определяет, откуда считывать данные, а знак > - куда выводить данных. При этом программа будет работать и без ввода/вывода в файл в обычном режиме ввода с клавиатуры и вывода на экран: C:\myfile.exe 12.Записи При рассмотрении матриц с разнородной информацией (строка, число, дата) лучше всего использовать структуру данных «запись». Запись - это структура данных, состоящих из фиксированного числа компонентов, называемых полями записи. В отличие от массива, компоненты (поля) записи могут быть различного типа. Чтобы можно было ссылаться на тот или иной компонент записи, поля именуются. Ключевым словом в описании записи является Record, после которого перечисляются все поля с указанием их типов, после которых обязательно слово End. Для обращения к полям записи используется разделитель «точка» между именем переменной и именем поля или оператор With <имя переменной> do <имена полей записи> в операторах. Задание №1. Введите данные о своих друзьях: Фамилия И.О., Дату рождения, Домашний адрес и телефон. По заданной дате установите, кого в этот день нужно поздравить с днем рождения. Const Kol=12; Type Birthday=Record Day:1..31; Month:1..12; Year:Word; End; Friend=Record Fio:String[25]; Data:Birthday; Adress:String[50]; Telephone:longint; End; Var I:integer; A:holiday; B:array[1..Kol] of friend; Begin 19 Write(‘Введите текущую дату(дд мм гггг):’); Readln(a.day,a.month,a.year); for i:=1 to Kol do with b[i] do begin readln(fio) readln(data.day, data.month, data.year); readln(address); readln(Telephone); end; for i:=1 to Kol do if (b[i].data.day=a.day) and (b[i].data.month=a.month) then writeln(‘Не забудь поздравить с днем рождения своего друга ’, b[i].fio, ‘ по телефону ’, b[i].telephone, ‘. Ему исполнилось ’,a.year-b[i].data.year, ‘ лет!’); End. Поля записи строятся в памяти по порядку их перечисления. Каждая запись состоит из такого количества байт, из которого состоит в сумме каждое поле этой записи. Эти записи можно хранить в текстовом файле и считывать их оттуда. С помощью записи строятся базы данных, динамические структуры данных: списки, очереди, деревья. Начало файла Блок с записью Размер файла (количество блоков) Условная граница 0 1 2 3 4 5 Обычно записи хранятся в типизированных файлах, которые хранят элементы в виде линейной последовательности блоков и предполагают к каждому элементу прямой доступ. Каждый блок представляет собой одну запись или компоненту того типа, с которым файл объявлен. Если файл содержит n блоков, то они нумеруются от 1 до n. Кроме того, вводится понятие условной границы между блоками (элементами), при этом условная граница с номером 0 расположена перед блоком с номером 1, граница с номером 1 расположена перед блоком с номером 2 и, наконец, условная граница с номером n находится после блока с номером n. Реализация прямого доступа осуществляется с помощью следующих функций и процедур: Применение Назначение L:=FileSize(f); Возвращает количество блоков в открытом файле f. P:=FilePos(f); Возвращает текущую позицию в файле f. Позиция в файле - это номер условной границы. Для только что открытого файла текущей позицией будет граница с номером 0. Это значит, что можно записать или прочесть блок с номером 1. После чтения или записи первого блока текущая позиция переместится на границу с номером 1, и можно будет обращаться к блоку с номером 2. После прочтения последней записи значение FilePos равно значению FileSize. 20 Seek(f, N); Truncate(f); Обеспечивает назначение текущей позиции в файле (позиционирование). В параметре N должен быть задан номер условной границы, предшествующей блоку, к которому будет производиться последующее обращение. Например, чтобы работать с блоком 4, необходимо задать значение N, равное 3. Процедура Seek работает с открытыми файлами. Устанавливает в текущей позиции признак конца файла, при этом все последующие блоки не доступны, т.е. считаются удаленными. Задание №2. Создайте базу данных с полями: Фамилия, Имя, Отчество учащегося, класс, оценки по предметам на экзаменах. Выведите средний балл каждого учащегося и каждого класса. Оформим запись следующим образом: Type Predmets=(Mathematika, Fizika, History, English, Russian); Ball=1..5; Study=record Klass: string[4]; Family, Name, Father: string[15]; Ocenka: array[Predmets] of ball; Average: real; End; Filerec=file of Study; Рассмотрим следующие методы работы с базой данных: 1) Проверить, пуста ли база данных. 2) Добавить в базу данных несколько записей 3) Просмотреть их для проверки 4) Подсчитать и вывести средний балл каждого учащегося 5) Подсчитать и вывести средний балл каждого класса Проверка на пустоту базы данных будем проверять по функции filesize. Но даже если база данных не пуста, нам все равно понадобиться количество уже введенных записей. Оформим работу через меню команд 1. Добавление записей 2. Просмотр записей 3. Очистить базу данных 4. Вывод среднего балла класса 5. Выход Это меню обрабатывается с помощью оператора case of. Приведем дальнейший текст программы: Var St: study; F: filerec; K, L, kol: integer; Menu:1..5; Answer, klass:string[4]; Flag: Boolean; Av: real; Begin Assign (f,’study.dat’); {$I+}Reset(f);{$I-} {если файл не создан} If IOResult<>0 then begin Rewrite(F);close(F);Reset(F); {то создадим пустой файл} 21 End; L:=filesize(f); If L=0 then Flag:=false else Flag:=true; Repeat {вывод меню команд} Writeln(‘Выберите номер команды:’); Writeln(‘1. Добавление записей’); Writeln(‘2. Просмотр записей’); Writeln(‘3. Очистить базу данных’); Writeln(‘4. Вывод среднего балла класса’); Writeln(‘5. Выход’); Readln(menu); Case menu of 1: begin ClrScr; Write(‘Введите данные об ученике:’); With st do begin Write(‘Класс -’);readln(klass); Write(‘Фамилия -’);readln(family); Write(‘Имя -’);readln(name); Write(‘Отчество -’);readln(father); Write(‘Введите данные об оценках:’); Write(‘Математика -’);readln(ocenka[mathematika]); Write(‘Физика -’);readln(ocenkafizika]); Write(‘История -’);readln(ocenka[history]); Write(‘Английский -’);readln(ocenka[English]); Write(‘Русский язык -’);readln(ocenka[Russian]); Average:=0; For k:=mathematika to Russian do average:=average+ocenka[k]; End;{with} Write(f,st); L:=filesize(f); Flag:=true; end; 2: begin If flag then For k:=1 to L do begin Seek(f,k-1);read(f,st); With st do begin Write(‘Класс -’);writeln(klass); Write(‘Фамилия -’);writeln(family); Write(‘Имя -’);writeln(name); Write(‘Отчество -’);writeln(father); Write(‘Оценки по предметам:’); Write(‘Математика-’);writeln(ocenka[mathematika]); Write(‘Физика-’);writeln(ocenkafizika]); Write(‘История -’);writeln(ocenka[history]); Write(‘Английский -’);writeln(ocenka[English]); Write(‘Русский язык -’);writeln(ocenka[Russian]); Write(‘Средний балл -’);writeln(average); 22 End;{with} End{for} Else begin writeln(‘Записей в базе данных нет!’);readln; end; End; 3: begin Write(‘Вы действительно хотите удалить все записи в базе данных? (Да/Нет)’);readln(answer); If answer=’Да’ then begin seek(f,0);truncate(f);L:=0; Flag:=false; end; end; 4: begin If flag then begin Write(‘Введите класс:’);readln(Klass); Av:=0;Kol:=0; For k:=1 to L do begin Seek(f,k-1);read(f,st); IF klass=st.klass then With st do begin Write(‘Фамилия -’);writeln(family); Write(‘Имя -’);writeln(name); Write(‘Отчество -’);writeln(father); Write(‘Оценки по предметам:’); Write(‘Математика-’);writeln(ocenka[mathematika]); Write(‘Физика-’);writeln(ocenkafizika]); Write(‘История -’);writeln(ocenka[history]); Write(‘Английский -’);writeln(ocenka[English]); Write(‘Русский язык -’);writeln(ocenka[Russian]); Write(‘Средний балл -’);writeln(average); Av:=av+average; Inc(kol); End;{with} End{for} Av:=av/kol; Write(‘Средний балл по классу-’);writeln(av); end Else begin writeln(‘Записей в базе данных нет!’);readln; end; end; 5: break; End; Until false; Close(f); End. Если вы заметили, то в этой программе нет массива записей. Мы работаем только с данными из файла, переходя от записи к записи последовательно с помощью процедуры seek. Такой подход экономит статическую память, при этом размер файла может достигать сколь угодно больших размеров. Можно продолжить работу с базой данных: 23 - сортировать по среднему баллу, фамилии И.О. учеников и классу, - редактировать данные по выбранному ученику, - удалять только выбранных учеников или класс, - выводить средний балл по предметам, - сохранять отчеты в текстовый файл, - и т.д. Это предлагаем сделать самостоятельно. Мы рассмотрели два подхода к формированию базы данных: через массив записей и через типизированные файлы. В каждом конкретном случае можно выбрать либо тот, либо другой подход. 13.Процедуры и функции пользователя Определить простейшую процедуру довольно просто: практически любой составной оператор (группа операторов, заключенных в операторные скобки, т.е. между ключевыми словами Begin … End;), вынесенный из основного блока программы и объявленный предложением PROCEDURE ИмяПроцедуры; становится процедурой, и вместо составного оператора в основном блоке может подставлятся одно лишь ИмяПроцедуры;. Согласно более общему определению, процедура может иметь параметры, метки перехода внутри себя, свои локальные переменные и процедуры. Обязательными элементами процедур и функций является заголовок и тело - составной оператор. Общая структура функций совпадает со структурой процедур, за исключением заголовка и оператора присваивания значения функции (в теле имя функции представляется как идентификатор переменной того типа, который стоит в заголовке функции после двоеточия). Он описывается так FUNCTION ИмяФункции (список параметров): <Скалярный Тип Значения Функции>; Синтаксис вызова процедур прост. Ее выполнение активизируется указанием ее имени и списком переменных или значений, подставляемых на место параметров: ИмяПроцедуры (Параметр1, Параметр2,…); Все процедуры и функции должны быть распределены по порядку обращения к ним, как будто нанизываем кольца на стержень. Для поиска нужной процедуры программа переберет все процедуры, стоящие снизу вверх от места вызова (а не сверху вниз!), как будто мы снимаем кольца со стержня по очереди. И затем будет выполнять все операции после заголовка до end. После этого программа вернет управление обратно в точку вызова и продолжит выполнение следующих после нее операторов. Примеры заголовков процедур и функций: PROCEDURE Swap (var a, b: integer); PROCEDURE Stop; FUNCTION Max (a, b: byte): byte; FUNCTION Sum (a, b: real): real; Примеры вызова процедур и функций: Swap (a, b); 24 Stop; a:= Max (c, d); t:= b+ sum (5, a); Объявление процедуры A1 Объявление процедуры A2 Вызов процедуры A1 Объявление процедуры A3 Тело программы Вызов процедуры А3; Вызов процедуры А2; Вызов процедуры А1; Параметры. Локальные и глобальные данные Параметры процедур и функций в заголовках называются формальными параметрами и должны содержать после идентификаторов переменных после двоеточия ставить ее тип. При обращении к функции или процедуре в списке параметров подставляются конкретные значения того же типа и в том же порядке или идентификаторы переменных того же типа и в том же порядке. В этом случае мы говорим о фактических параметрах. Как же передаются значения в процедуры и функции? Первый способ – по значению, т.е. передается значение или значение фактической переменной самой формальной переменной (как будто до вызова процедуры выполнился невидимый оператор присваивания), у же в процедуре это значение используется через эту переменную. Как будто сделали копию переменной в памяти компьютера. Так оно и есть – на каждый вызов процедуры программа занимает дополнительно ровно столько места, сколько нужно для размещения формальных переменных. Такие параметры называются параметры-значения. Второй способ – по ссылке, т.е. передается только адрес памяти, где размещается фактическая переменная. Тогда формальная переменная и фактическая будут работать с одним и тем же значением, и память не будет выделять под нее дополнительное место. Если значение переменной в процедуре изменится, то изменится и в вызывающем процедуру блоке программы. При этом первоначальное значение фактической переменной потеряется. В таком случае нельзя ставить в списке формальных параметров значение, а только его идентификатор. В списке формальных параметров перед идентификатором ставится ключевое слово VAR. Такие параметры называются параметры-переменные. Пример использование параметров- значений и параметров- переменных : PROGRAM Prim; VAR a : Integer; 25 PROCEDURE Less (x : Integer); {параметр- значение} BEGIN x:= x-1; {В процедуре значение параметра уменьшается на 1} END; BEGIN a:=5; Writeln ( ‘a=’, a); Less(a); Write (‘новое значение a=’, a); END. После выполнения этой программы компьютер выдает: a= 5 новое значение a= 5 Это происходит потому, что единственный параметр процедуры Less есть параметрзначение. Изменим в приведенном выше примере заголовок процедуры: PROCEDURE Less (Var x : Integer); В этом случае после выполнения программы компьютер выдает: a= 5 новое значение a= 4 Этот пример показывает, что, в отличии от параметров- значений, параметрыпеременные связываются с фактическими параметрами в момент вызова и остаются связанными с ними пока выполняется процедура. Глобальные константы, типы, переменные- это те, которые объявлены в программе вне процедур и функций. Локальными называют константы, переменные и типы, существующие только внутри процедур и функций, и объявленные либо в списке параметров (только переменные), либо в разделах CONST, TYPE,VAR внутри процедуры или функции. Процедуры и функции могут, наряду со своими локальными данными, использовать и модифицировать и глобальные. Для этого нужно лишь, чтобы описание процедуры (функции) стояло в тексте программы ниже, чем описания соответствующих глобальных типов, констант и переменных: PROGRAM Main; {глобальные переменные} VAR Xmain, Ymain : LongInt; Res: Real; PROCEDURE Proc1 (a, b : Word; VAR Result : Real); VAR Res : Real; {Локальная переменная, закрывающая глобальную} BEGIN Res := a*a + b*b; {локальные действия} Result := Xmain+Ymain*Res; {работают глобальные значения} Xmain:= Xmain+1; {модифицируется глобальное значение} END; {Другие глобальные объявления, уже недоступные из процедуры Proc1} TYPE ... CONST ... 26 VAR ... BEGIN {Основной блок, в котором может вызываться Proc1} END. При совпадении имен глобальной и локальной переменной (типов, констант) сильнее оказывается локальное имя, и именно оно используется внутри подпрограммы. Существует неписанное правило: если подпрограмма содержит в себе циклы FOR ,то параметры циклов должны быть описаны как локальные переменные. Это предупредит неразбериху при циклическом вызове процедур. Приведем еще один пример использования параметров – переменных и параметров – значений: {программа вычисляет А в кубе, где А- матрица порядка n x n, n<=5 } PROGRAM Kub; TYPE matrica= Array [ 1..5,1..5] of Integer; VAR n, i, j : Integer; a, b, c : matrica; PROCEDURE Multiply (n: Integer; a, b : matrica; VAR c : matrica); {данная процедура находит С=А*В – произведение двух матриц} {параметры n, a, b являются параметрами – значениями, с – параметр - переменная} VAR i, j, k, s : Integer; BEGIN FOR i:=1 TO n DO FOR j:=1 TO n DO BEGIN s:=0; FOR k:=1 To n DO s:=s + a[i ,k] * b[k,j]; c[i,j]:=s; END; {конец цикла} END; {конец процедуры Multiply} {печать матрицы, используются параметры - значения } PROCEDURE Prn (n: Integer; a: matrica); VAR i, j :Integer; BEGIN FOR i: =1 TO n DO BEGIN FOR j:=1 TO n DO WRITE (a[i, j] : 5); WRITELN; END;{конец цикла} END; {конец процедуры} BEGIN WRITE (‘введи размерность матрицы А,<=5’); READLN(n); WRITELN (‘матрица А’); 27 FOR i:=1 TO n DO {задаем элементы матрицы с помощью} FOR j:=1 TO n DO {датчика случайных чисел} a[i, j]:= random(10); Prn (n, a); {печатаем матрицу А} Multiply (n, a, a, b); {находим В=А*А} Multiply (n, a, b, c); {находим С=А*В – куб матрицы А} WRITELN (‘C=A*A*A); Prn (n, c); {печатаем матрицу С} END. Большое значение имеет соблюдение правил соответствия типов при подстановке параметров. Число и порядок следования параметров в вызове должен соответствовать описанию процедуры или функции. Кроме того, в Турбо Паскале существует правило, требующее, чтобы параметры, имеющие файловый (или сложный тип с файловыми компонентами), были обязательно описаны как VAR- параметры. Процедуры и функции могут быть вложенными друг в друга. Число уровней вложенности может быть достаточно большим, но на практике не превышает второго уровня. Вложенная процедура или функция относится к охватывающей ее программе так же, как сама программа относится к основной программе. Вложенные процедуры и функции могут вызываться только внутри охватывающей программы. Область действия меток переходов всегда локальна, и нельзя планировать переходы с помощью оператора Goto из вложенной процедуры в охватывающую или основной блок программы, или из программы в процедуру. Рекурсивные подпрограммы Использование рекурсии – традиционное преимущество языка Паскаль. Турбо Паскаль в полной мере позволяет строить рекурсивные алгоритмы. Под рекурсией понимается вызов функции (процедуры) из тела этой же самой функции (процедуры). Рекурсивность часто используется в математике. Так, многие определения математических формул рекурсивны. В качестве примера можно привести формулу вычисления факториала: n!=1, если n=0 n!= n*(n-1)! , если n>0 Видно, что для вычисления каждого последующего значения нужно знать предыдущее. В Паскале рекурсия записывается так же, как и в формулах. Ниже приведен пример реализации функций вычисления того же факториала: FUNCTION Fact (n : Word) : LongInt; BEGIN If n=0 Then Fact :=1 Else Fact := n*Fact (n-1); END; Если в функцию передаются n>0 , то происходит следующее: запоминаются известные значения членов выражения в ветви ELSE, а для вычисления неизвестных вызываются те же функции, но с «предшествующими» аргументами. При этом вновь запоминаются (но уже в другом месте памяти!) известные значения членов и происходят вызовы. Так происходит до тех пор, пока выражение не станет полностью определенным (в нашем примере – это присваивание в ветви THEN), после чего алгоритм начинает раскручиваться в обратную сторону, изымая из памяти отложенные значения. Поскольку при этом на каждом очередном шаге все члены выражений уже будут известны, через n таких «обратных» шагов мы получим конечный результат. 28 Необходимым условием для работоспособности рекурсивных процедур является наличие условия окончания рекурсивных вызовов (например, проверка значения изменяющегося параметра). Действия, связанные с такой проверкой, уже не могут содержать рекурсивных вызовов. Если это условие не будет выполняться, то глубина рекурсии станет бесконечной, что неизбежно приведет к аварийному останову программы. Зачастую внесение рекурсивности в программы придает им изящность. Но всегда оно же «заставляет» программы расходовать больше памяти. Дело в том, что каждый «отложенный» вызов функции или процедуры – это свой набор значений всех локальных переменных этой функции, размещенных в стеке. Если будет, например, 100 рекурсивных вызовов функции, то в памяти должно разместиться 100 наборов локальных переменных этой функции. Поэтому во многих случаях те же задачи более эффективно решаются «итерационными» методами, не требующими «лишней» памяти при сопоставимой скорости вычислений. Задание №1. Используя процедурный подход, создайте систему управления базой данных, включающей в себя ввод, редактирование, просмотр и статистическую обработку данных записей, состоящих из следующих полей: день, месяц, год, температура воздуха, давление, влажность, скорость и направление ветра, облачность, осадки. Вычислить среднегодовые и среднемесячные данные по введенным числовым данным. 14.Модуль CRT Модуль-это библиотека процедур, функций, типов, констант, собранных в один файл. При работе с выводом текстовой информации на экран часто используют процедуры и функции модуля CRT (электронно-лучевая трубка). В работе часто используют цветовые параметры. Приведем константы для задания цвета: Константа Black Blue Green Cyan Red Magenta Brown LightGrey DarkGrey LightBlue LightGreen LightCyan LightRed LightMagenta Yellow White Значение 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Цвет Черный Синий Зеленый Бирюзовый Красный Сиреневый Коричневый Светло-серый Темно-серый Голубой Светло-зеленый Светло-бирюзовый Светло-красный Светло-сиреневый Желтый Белый Приведем процедуры и функции модуля CRT: Процедура ClrEоl; ClrScr; Назначение Очищает все символы, начиная от позиции курсора до конца строки, без перемещения курсора Очищает экран и помещает курсор в верхнем левом углу. 29 Процедура Dеlау(<миллисекунды>) DelLine; GоtоХY(X, Y) НightVideo; InsLine; =KeyРrеssеd; NoSound; Sound(<частота в Гц>); TextВаckground(<цвет>); TextColor(<цвет>): TextМоdе(<режим>) Window(X1,Y1, X2,Y2) =Rеаdкеу; =WherеХ; =WherеY; Назначение Выполняет задержку на указанное число миллисекунд Удаляет строку, на которой находится курсор и перемещает все следующие строки на одну строку вверх. Нижняя строка очищается. Выполняет позиционирование курсора. Х – это горизонтальная позиция, Y - вертикальная позиция. Выбирает символы с подсветкой Вставляет пустую строку в месте расположения курсора. Возвращает значение Truе, если клавиша на клавиатуре нажата и Falsе - в противном случае. Выключает внутренний динамик Включает внутренний динамик Выбирает фоновый цвет. Выбирает цвет самого символа. Выбирает конкретный текстовый режим Определяет на экране текстовое окно. Считывает символ с клавиатуры Возвращает координату Х для текущей позиции курсора, относящуюся к текущему окну. Х представляет собой горизонтальную позицию Возвращает координату Y для текущей позиции курсора, относящуюся к текущему окну. Y представляет собой вертикальную позицию Задание №1. Выведем светофор, используя мигание слов соответствующим цветомКРАСНЫЙ, ЖЕЛТЫЙ, ЗЕЛЕНЫЙ. Uses CRT; begin repeat clrscr; textcolor(red+blink); writeln(‘красный’); textcolor(yellow); writeln(‘желтый’); textcolor(green); writeln(‘зеленый’); delay(1000); clrscr; textcolor(red); writeln(‘красный’); textcolor(yellow+blink); writeln(‘желтый’); textcolor(green); writeln(‘зеленый’); delay(1000); clrscr; textcolor(red); writeln(‘красный’); textcolor(yellow); writeln(‘желтый’); 30 textcolor(green+blink); writeln(‘зеленый’); delay(1000); until keypressed; end. Измените программу, используя цикл. Задание №2. Введем пароль, отображаемый в виде звездочек. Если ввели верно, то выведем сообщение, иначе заново будем его вводить. Uses CRT; Cont password=’Rita’; Var C:char; p:string[10]; Begin P:=’’; While p<>password do begin Clrscr; P:=’’; Write(‘введите пароль:’); Repeat C:=readkey; If c<>#13 then begin Write(‘*’); P:=p+c; End; Until ord(c) =13; {код клавиши Enter} Readln; End; Write(‘пароль введен верно!’);readln; End. Задание №3. Выведите код нажатой клавиши до нажатия клавиши Esc. Uses CRT; Var C:char; Begin Repeat C:=readkey; Writeln(c,’-‘,ord(c)); Until ord(c) =27{код клавиши Esc } End. Если нужен код управляющей клавиши, то используем вызов Readkey дважды, так как эти клавиши хранят в первом слове 0, а только во втором слове второй код. Uses CRT; Var C:char; Begin Repeat C:=readkey; If ord(c)=0 then c:=readkey; Writeln(c,’-‘,ord(c)); 31 Until ord(c) =27{код клавиши Esc } End. Эти коды используют для перемещения по пунктам меню, для перемещения текста и других объектов относительно экрана. Задание №4. Используя клавиши управления курсором, переместите набранный текст по экрану. Uses CRT; Const s=’Rita’; Var C:char; X,y:integer; Begin X:=1;y:=1; Repeat Clrscr; C:=readkey;c If ord ( c )=0 then c:=readkey; Case ord( c ) of 78:inc(x); 79:dec(x); 80:inc(y); 81:dec(y); End; If ((x<80)and(x>0)) and((y<25)and(y>0)) then Gotoxy(x,y,s) else beep; Until ord(c) =27{код клавиши Esc } End. 15.Модуль Graph Для вывода графической информации используется графический режим вывода попиксельно, а не по-символьно. При этом используется библиотека процедур и функций модуля GRAPH. Для инициализации графического экрана нужно загрузить в память драйвера (файлы .BGI) для обработки графической информации. Эти файлы расположены в папке BGI. Этот путь к папке нужно указать в процедуре инициализации InitGraph. Драйвера CGA.BGI EGAVGA.BGI HERC.BGI ATT.BGI PC3270.BGI IBM8514.BGI Графический режим драйвер для IBM CGA, MCGA драйвер для IBM EGA, VGA драйвер для монохромного Hercules драйвер для ATT&T 6300 (400 строк) драйвер для IBM 3270 PC драйвер для IBM 8514 Каждый драйвер содержит код и данные, хранящиеся в отдельном файле. Во время выполнения процедура InitGraph определяет графическое устройство, загружает и инициализирует соответствующий графический драйвер, переводит систему в графический режим и возвращает управление вызывающей программе. Процедура CloseGraph выгружает драйвер из памяти и восстанавливает предыдущий видеорежим. Вы можете переключиться между графическим и текстовым режимами, используя процедуры 32 RestoreCrtMode (восстановить текстовый режим) и SetGraphMode (восстановить графический режим). Graph поддерживает конфигурацию с двумя мониторами. Когда Graph инициализируется вызовом InitGraph, соответствующий монитор будет выбран для запрошенного графического драйвера и режима. Когда графическая программа завершается, предыдущий видеорежим будет восстановлен. Если в конфигурации с двумя мониторами будет вызван InitGraph c режимом авто-обнаружения, Graph выберет монитор и графический адаптер, на котором будет поддерживаться графический вывод с более высоким качеством. Адаптеры AT&T400 и IBM8514 нельзя распознать автоматически. Для использования этих драйверов Вам нужно отменить авто обнаружение и передать в InitGraph код драйвера и правильный код графического режима. Константа Для переменной GraphDriver Detect CGA MCGA EGA EGA64 EGAMono IBM8514 HereMono AT&T400 VGA PC3270 CurrentDriver Для переменной GraphMode CGAC0 Значение Описание Запрашивает автообнаружение 0 1 2 3 4 5 6 7 8 9 10 -128 Передается в GetModeRange 320x200 палитра 0:LightGreen, LighhtRed,Yellow;1 страница 320x200 палитра 1:LightCyan, LightMegenta,White;1 страница 320ч200 палитра 2:Green,Red,Brown; 320x200 палитра 3:Cyan,Magenta, LightGray;1 страница 640x200 1страница 0 CGAC1 1 CGAC2 CGAC3 2 3 CGAHi 4 MCGAC0 0 MCGAC1 1 MCGAC2 MCGAC3 2 3 MCGAMed MCGAHi EGALo EGAHi EGA64Lo EGA64Hi EGAMonoHi 4 5 0 1 0 1 3 320x200 палитра 0:LightGreen, LightRed,Yellow;1страница 320x200 палитра 1:LightCyan, LightMagenta,White;1 страница 320x200 палитра 2:Green,Red,Brown; 320x200 палитра 3:Cyan,Magenta, LightGray;1 страница 640x200 1 страница 640х480 1 страница 640х200 16 цветов 4 страницы 640х350 16 цветов 2 страницы 640х200 16 цветов 1 страница 640х350 4 цвета 1 страница 640х350 64К: 1 страница 33 Константа Значение HereMonoHi AT&T400C0 0 0 AT&T400C1 1 AT&T400C2 2 AT&T400C3 3 AT&T400Med AT&T400Hi VGALo VGAMed VGAHi PC3270Hi IBM8514Lo IBM8514Hi 4 5 0 1 2 0 0 1 Описание 256К: 2 страницы 720х348 2 страницы 320х200 палитра 0: LightGreen, LightRed, Yellow; 1 страница 320х200 палитра 1: LightCyan, LightMagenta, White; 1 страница 320х200 палитра 2: Green, Red, Brown; 1 страница 320х200 палитра 3: Cyan, Magenta, LightGrey; 1 страница 640х200 1 страница 640х400 1 страница 640х200 16 цветов 4 страницы 640х350 16 цветов 2 страницы 640х480 16 цветов 1 страница 720х350 1 страница 640х480 256 цветов 1024х768 256 цветов Простой пример программы, использующий графический вывод на монитор: uses Graph; var GraphDriver, GraphMode, ErrorCode: Integer; begin GraphDriver:=Detectt; {режим автоопределения} {инициализация графического режима} InitGraph(GraphDriver, GraphMode, ‘C:\BP\BGI’); ErrorCode:=GraphResult; if ErroeCode<>grOk then {ошибка} begin Writeln(‘Graphics error:’,GraphErrorMsg(ErrorCode)); Writeln(‘Program aborted...’); Halt(1); end; setcolor(red); Rectangle(0,0,GetMaxX, GetMaxY); Line(0,0,GetMaxX, GetMaxY); Readln; CloseGraph; end. После инициализации графического экрана используются команды и функции модуля GRAPH для рисования простых геометрических фигур, вывода текста, установки цветов, типа линий и заливки и другие. Процедура Назначение Процедуры Arc (x, y: integer; StartAngle, EndAngle, Рисует дугу от начального угла к конечному, Radius: Word); Используя (X, Y) как центр 34 Процедура Bar (x1, y1, x2, y2: integer); Bar3D(x1, y1, x2, y2: integer; depth:word; top: boolean); Circle (x, y: Integer; Radius: Word); ClearDevice; ClearViewPort; CloseGraph; DetectGraph( var graphdriver, graphmode: integer); DrawPoly( NumPoints: word; var PolyPoints); Ellipse (X, Y: Integer; StartAngle, EndAngle, Xradius, Yradius: Word); FillElilpse (X, Y: Integer; Xradius, Yradius: Word); FillPoly( NumPoints: word; var PolyPoints); FloodFill(X,Y:integer; Border: word); GetArcCoords GetAspectRatioi GetFillPattern GetFillSettings GetImage GetLineSettings GetModeRange GetPallete GetTextSettings GraphDefaults GetViewSettings InitGraph Line (x1, y1, x2, y2: Integer) LineRel LineTo (x, y: Integer) Назначение Рисует полосу, используя текущий стиль и цвет Рисует трехмерную полосу, используя текущий стиль и цвет рисует окружность, используя (X, Y) как центр Очищает экран и устанавливает текущий указатель (CP) в начало Очищает окно Закрывает графическую систему Проверяет аппаратуру и определяет какой графический драйвер и в каком режиме используется рисует многоугольник текущим цветом и типом линии рисует эллиптическую дугу от начального угла к конечному, используя (X, Y) как центр рисует заполненный эллипс, используя (X, Y) как центр и Xradius и Yradius как горизонтальные и вертикальные оси заполняет многоугольник, используя сканирование заполняет ограниченную область, используя текущий шаблон и цвет заполнения позволяет запросить координаты команды Arc возвращает разрешение экрана, из которого может быть вычислен относительный аспект (Xasp, Yasp) возвращает шаблон заполнения, установленный последним вызовом SetFillPattern позволяет запросить текущий шаблон и цвет, установленные SetFillStyle и SetFillPattern сохраняет битовый образ указанной части экрана в буфере возвращает текущий стиль, шаблон и толщину линии, установленные SetLineStyle возвращает минимальный и мак. граф. режимы для данного драйвера возвращает текущую палитру и ее размер возвращает текущий шрифт, направление, размер и выравнивание текста, установленные SetTextStyle и SetTextJustufi устанавливает текущий указатель (CP) в начало и переустанавливает графическую систему позволяет запросить текущие параметры окна и отсечения инициализирует графическую систему и устанавливает устройство в граф. режим рисует линию от (X1, Y1) к (X2, Y2) рисует линию от текущего указателя (CP) к точке, лежащей на заданном расстоянии рисует линию от текущего указателя к (X, Y) 35 Процедура MoveRel MoveTo (x, y: Integer) OutText (TextString: String) OutTextXY (x, y: Integer; TextString: String) PieSlice PutImage PutPixel (x, y: Integer; Color: Word) Rectangle (x1, y1, x2, y2: Integer) RestoreCRTMode Sector SetActivePage SetAllPalette SetAspectRatio SetBkColor SetColor SetFillPattern SetFillStyle SetGraphBufSize SetGraphMode SetPalette SetRGBPalette SetTextJustify SetTextStyle SetLineStyle SetUserCharSize SetViewPort SetVisualPage SetWriteMode GetBkColor GetColor Назначение передвигает текущий указатель (CP) на заданное расстояние от его текущей позиции передвигает текущий указатель (CP) в (X, Y) выводит текст на экран от текущего указателя выводит текст на экран рисует и заполняет сектор, используя (X, Y) как центр и рисуя от начального угла к конечному выводит битовый образ на экран рисует точку (пиксел) в (X, Y) рисует прямоугольник , используя текущий цвет и тип линии Восстанавливает видеорежим, который был до инициализации графики рисует и заполняет сектор эллипса Устанавливает активную страницу для граф. Вывода изменяет цвет палитры изменяет значение относительного аспекта Устанавливает цвет фона Устанавливает основной цвет, которым будет осуществляться рисование выбирает шаблон заполнения, определенный пользователем Устанавливает шаблон заполнения и цвет Позволяет изменить размер буфера для функций заполнения переводит систему в графический режим и очищает экран изменяет один цвет палитры, указанный через ColorNum и Color позволяет модифицировать палитру для IBM 8514 и VGA устанавливает выравнивание текста, используемое OutText и OutTextXY устанавливает текущий шрифт, стиль и размер текста устанавливает текущие толщину и стиль линии позволяет изменить ширину и высоту символа для штрихового шрифта устанавливает текущее окно для графического вывода устанавливает номер видимой графической страницы устанавливает режим вывода (копирование или XOR ) для линий, рисуемых с DrawPoly, Line, LineRel, LineTo, Rectangle Функции возвращает текущий фоновый цвет возвращает текущий цвет 36 Процедура GetDefaultPalette GetDriverName GetGraphMode GetMaxColor GetMaxMode GetMaxX GetMaxY GetModeName GetPaletteSize GetPixel GetX GetY GraphErrorMsg GraphResult ImageSize InstallUserDriver InstallUserFont RegisterBGIDriver RegisterBGIFont TextHeight (TextString: String) TextWidth (TextString: Srting) Назначение возвращает аппаратную палитру в записи PaletteType возвращает строку с именем текущего драйвера возвращает текущий графический режим возвращает максимальный цвет, который можно задать в SetColor возвращает номер максимального режима текущего загруженного драйвера возвращает максимальный X (разрешение по горизонтали) для текущего графического драйвера и режима возвращает максимальный Y (разрешение по вертикали) для текущего графического драйвера и режима возвращает строку с именем указанного графического режима возвращает размер таблицы палитры возвращает цвет точки в (X, Y) возвращает координату X текущей позиции (CP) возвращает координату Y текущей позиции (CP) возвращает строку сообщения об ошибке для заданного кода ErrorCode возвращает код ошибки для последней графической операции возвращает число байт, требуемое для заполнения прямоугольной области экрана устанавливает пользовательский драйвер устройства в BGI таблицу драйверов устройств устанавливает новый шрифт, который не встроен в BGI систему регистрирует драйвер BGI для графической системы регистрирует шрифт BGI для графической системы возвращает высоту строки в пикселях возвращает ширину строки в пикселях Цветовая палитра используется такая же, как и в модуле CRT. Приведем некоторые константы, которые используются при установке типа линий и типа заливки: Константа Значение Для значения поля LineStyle SolidLn 0 DottedLn 1 CenterLn 2 DashedLn 3 UserBitLn 4 Для значения поля Thickness Назначение Сплошная линия Точечная линия Штрих пунктирная линия Пунктирная линия Тип линии, определяемый пользователем 37 NormWidth 1 ThickWidth 3 Для значения заливки Pattern EmptyFill 0 SolidFill 1 LineFill 2 LtSlashFill 3 SlashFill 4 BkSlashFill 5 LtBkSlashFill 6 HatchFill 7 XhatchFill 8 InterleaveFill 9 Wide DotFill 10 CloseDotFill 11 UserFill 12 Толщина линии в один пиксел Толщина линии в три пикселя Заполняет цветом фона Заполняет основным цветом --- заполнение /// заполнение /// заполнение толстыми линиями \\\ заполнение толстыми линиями \\\ заполнение Редкая штриховка Плотная штриховка Пересекающиеся линии Редкие точки Плотные точки Определенный пользователем стиль Задание №1. Построим разноцветную звезду. Uses graph; Var x0,y0,r,i:integer; Fi:real; X,y: array[0..4] of integer; graphdriver, graphmode:integer; Begin {инициализация графического режима} Graphdriver:=Detect; Initgraph(graphdriver, graphmode, ‘C:\BP\BGI’); X0:=200;Y0:=200;R:=100;Fi:= 2*pi/5; For i:=0 to 4 do begin X[i]:=round(x0+R*cos(i*Fi)); Y[i]:=round(y0-R*sin(i*Fi)); End; Setcolor(red); Line(x[0],y[0],x[2],y[2]); Line(x[2],y[2],x[4],y[4]); Line(x[4],y[4],x[1],y[1]); Line(x[1],y[1],x[3],y[3]); Line(x[3],y[3],x[0],y[0]); Setfillstyle(1,red); Floodfill(x0,y0,red); Readln; Closegraph; End. Раскрасьте самостоятельно все лучи звезды разными цветами. Для построения звезды с заливкой одного цвета используйте процедуру FillPoly. Uses graph; Var x0,y0,r,i:integer; Fi:real; Z:array[0..5]of Point; graphdriver, graphmode:integer; Begin {инициализация графического режима} 38 Graphdriver:=Detect; Initgraph(graphdriver, graphmode, ‘C:\BP\BGI’); X0:=200;Y0:=200;R:=100;Fi:= 2*pi/5; For i:=0 to 5 do begin Z.X[i]:=round(x0+R*cos(i*Fi)); Z.Y[i]:=round(y0-R*sin(i*Fi)); End; Setcolor(red); Setfillstyle(1,red); FillPoly(Z,6,red); Readln; Closegraph; End. Задание №2. Постройте разноцветную радугу, используя процедуру Sector. Задание №3. Построим график функции у=1/х на заданном отрезке. Для этого нам нужна информация о соотношении графического экрана (x1, y1, x2, y2) с реальной системой координат (a, ymax, b, ymin). Для этого нужно соотнести пиксельный шаг шагу на заданном отрезке StepX:= (b-a)/(x2-x1). Тогда, перебирая попиксельно каждую точку, будем вычислять значение функции в xx:=xx+stepx. Значение по оси Y будет меняться в обратном направлении, поэтому пиксельное значение нужно привести в соответствие к реальному yy:=1/xx так y:=round(y1+my*(ymax-yy)), где масштаб изменения по оси у вычисляется по формуле my:=(y2-y1)/(ymax-ymin). Для этого алгоритма нужно предварительно вычислить минимально и минимальное значение функции y=1/x. Так как функция имеет разрыв в точке 0, то при вычислении функции будем проверять это условие. Uses graph; Var x1,x2,y1,y2,x,y:integer; A,b,xx,yy,stepx,my,ymin,ymax:real; graphdriver, graphmode:integer; Begin Write(‘введите отрезок:’); readln(a,b); {инициализация графического режима} Graphdriver:=Detect; Initgraph(graphdriver, graphmode, ‘C:\BP\BGI’); X1:=10; x2:=getmaxx-10; Y1:=10; y2:=getmaxy-10; StepX:=(b-a)/(x2-x1); {вычисляем ymax, ymin} xx:=a; ymin:=1/xx;ymax:=ymin; for x:=x1 to x2 do begin xx:=xx+stepx; if xx<>0 then yy:=1/xx; if yy>ymax then ymax:=yy; if yy<ymin then ymin:=yy; end; {вычисляем масштаб по оси Y} my:=(y2-y1)/(ymax-ymin); Xx:=a; For x:=x1 to x2 do 39 Begin if xx<>0 then yy:=1/xx; y:=round(y1+my*(ymax-yy)); xx:=xx+stepx; PutPixel(x, y, red); End; Readln; closeGraph; End. Самостоятельно оформите график дальше. Например: постройте оси координат, если они есть. Иначе постройте их по краю области графика. Обозначьте оси координат. Сделайте разметку осей Х и У по заданной шкале. Выведите сетку. Проставьте числовые значения шкалы по осям Х и У, а также укажите граничные значения. Сделайте рамку области вывода графика. Вставьте название функции вне области рисования графика. Задание №4. Используя описание процедуры для построения разноцветной звезды, выведите миллиарды разных звезд на небе. Задание №5. Используя описание функций и процедуру для построения графика функции, постройте графики температур в разных городах за прошедший месяц. Используйте базу данных из типизированного файла. Задание №6. Используя процедуру вывода элементов массива в файл, функцию нахождения максимального и минимального значения элементов массива, постройте квадрат, включающий все точки, заданные своими координатами случайным образом. Затем еще один внутри первого по тому же правилу, исключая первые найденные и т.д. Примените рекурсивный подход. Мультипликация При создании графических объектов с ними можно проводить эффекты движения или мультипликации. Для этого можно применять следующие подходы: 1) использование перерисовки объекта цветом фона 2) сохранять фрагменты движущихся объектов в динамической памяти 3) использовать смену видеостраниц Приведем последние два способа, первый из них попробуйте реализовать самостоятельно. Для удобства процедуру инициализации напишем в отдельном файле initgraf.pas, который будем присоединять через ключ компиляции {$I initgraf.pas}: {содержимое файла initgraf.pas} Var graphdriver, graphmode, ErrorCode:integer; Procedure GrInit; Begin {инициализация графического режима} Graphdriver:=Detect; Initgraph(graphdriver, graphmode, ‘C:\BP\BGI’); ErrorCode:=GraphResult; if ErroeCode<>grOk then {ошибка} begin Writeln(‘Graphics error:’,GraphErrorMsg(ErrorCode)); Writeln(‘Program aborted...’); Halt(1); end; End; 40 Управление видеостраницами Память видеоадаптеров разделена на так называемые страницы, или видеостраницы. По умолчанию в графическом режиме действия производятся с нулевой страницей, поэтому фактически во всех предыдущих примерах было видно, как рисуется на экране фигуры. Однако если направить вывод изображений ненулевую страницу (при условии, что такая доступна в текущем режиме видеоадаптера), то на экране ничего не отобразится, поскольку по умолчанию видимой является нулевая страница. Если же после этого дать команду считать видимой «скрытую» страницу, то она появится на экране буквально мгновенно (конкретно за один прямой проход луча в кинескопе). Проделать все это позволяют две процедуры: SetVisualPage (Page: Word) которая устанавливает “видимой” на экране видеостраницу номер Page, и процедура SetActivePage (Page: Word) устанавливающая перенаправление всех графических операций на страницу номер Page. Приведем пример использования этих процедур. (*Пример только для адаптеров EGA и VGA !!!*) USES Graph, CRT; {$I initgraf.pas} PROCEDURE Forms (kadr: Byte); {рисование кадров 0..3} CONST Radius: Array [0..3] of Integer =(20, 40, 60, 80); VAR R, rr: Integer; {радиус эллипсов в квадратах} BEGIN R = Radius [kadr]; {максимальный радиус) RR = 0; {радиус вложенного эллипса} Repeat Ellipse (GetMaxX div 2, GetMaxY div 2, 0, 360, R, rr); Inc (rr, 5) Until rr >=R; END; PROCEDURE AnimEGAVGA; {процедура смены кадров} CONST ms=60; {задержка между кадрами, мс} VAR i: = Byte; {параметр циклов смены} BEGIN Repeat {цикл до нажатия клавиши..} For i:=0 to 3 do begin {смена видеостраниц: прямо} SetVisualPage (i); Delay (ms); End; For i:= 3 downto 0 begin {..и обратно} SetVisualPage(i); Delay (ms); End; Until KeyPressed; {условие окончания показа} END; VAR {*ОСНОВНАЯ ЧАСТЬ ПРИМЕРА*} I :Byte; BEGIN {параметр (номер кадра)} 41 GrInit; SetGraphMode (EGALo); For i: = 3 downto 0 do begin SetVisualPage (Succ (i) SetActivePage (i); Forms (i); End; {for} AnimEGAVGA; CloseGraph; {инициализация графики} {режим EGA, 640x200} {цикл заполнения страниц:} mod 4);{видим «пустоту»} {и готовим кадр} {рисунок кадра} {начало «оживления кадров} {закрытие режима графики} END. Здесь показано использование процедур SetActivePage и SetVisualPage для алгоритма «кадровой» мультипликации. Особенность ее заключается в том, что все кадры (здесь их четыре) сначала записываются на соответствующие страницы, а затем производится последовательное переключение отображения страниц на дисплей процедурой SetVisualPage. Работа с фрагментами изображений При работе с фрагментом всегда важно знать его объем в байтах. Функция ImageSize (x1,y1,x2,y2:integer): Word; возвращает размер памяти в байтах, необходимой для сохранения прямоугольной области экрана. Прямоугольник определяется координатами левого верхнего (x1,y1) и правого нижнего (x2,y2) углов. Эта функция обычно используется совместно с процедурой GetMem. Записать изображение в буфер можно, используя процедуру GetImage(x1,y1,x2,y2:integer;Var BitMap) в которой параметры x1,y1,x2,y2 имеют то же значение что и в ImageSize, а вместо бестипового параметра BitMap должна подставляться переменная-буфер, занимающая область памяти размера, необходимого для полного сохранения изображения. Отметим, что максимальный размер сохраняемого изображения не должна превышать 64К. Процедура PutImage (x1,y1:Integer; Var BitMap; Mode:Word) восстанавливает изображение из буфера BitMap в прямоугольник, левый верхний угол которого определен координатами (x, y). Обратите внимание на то, что в отличие от процедуры GetImage здесь нужна всего одна точка. Объясняется это тем, что в структуре BitMap первые два слова содержат ширину и высоту в пикселах запомненного изображения. Наиболее интересным в этой процедуре является возможность определять режим вывода изображения: можно суммировать изображение на экране и изображение в буфере, можно уничтожать изображение, содержащиеся в определенной области, можно инвертировать изображение, содержащееся в буфере. Эти операции задаются параметром Mode, для которого в модуле Graph определены константы, уже названные при описании процедуры SetWriteMode. Напишем их еще раз: CONST CopyPut=0; {операция Mov (замещение)} XORPut=1; {операция XOR} ORPut=2; {операция OR} ANDPut=3; {операция AND} NOTPut=4; {операция NOT} Например, если в режиме ORPut на малиновый цвет изображения вывести бирюзовый из буфера, то результирующая картинка будет светло-серого цвета. Из этих пяти режимов самым интересным является XOR, поскольку проведенные последовательно две операции XOR с одинаковым вторым аргументом оставляют первый из них без изменений. Это свойство операции XOR и используется в тех случаях, когда 42 необходимо изобразить некий подвижный объект на сложном фоне: два раза выведется один и тот же фрагмент в одном и том же месте в режиме XOR, мы оставим фон неизменным. Фактически, мы, таким образом, экономим память компьютера – не нужен буфер для запоминания участка фона, находившегося под новой картинкой. USES Graph, CRT; {используется Graph и CRT} {$I initgraf.pas} {вставляем процедуру инициализации} CONST R=10; {радиус подвижного шарика} VAR {переменные для оживления фрагмента} X1, Y1, X2, Y2, Sx, Sy: integer; maxx, maxy, sxmove, symove: integer; {величина фрагмента} Size: Word; {размер фрагмента} P: Pointer; {указатель на буфер} BEGIN GrInit; {инициализация графики} max: = GetMaxX; {максимальное поле экрана} max: = GetMaxY; X1: = maxx div 2 – R;{координаты области экрана,} X1: =maxy div 2 – R; {в которой будет нарисован} X2: =X1 + 2 * R; {шарик и которая будет со-} Y2: =Y1 + 2 * R; {храненным фрагментом} Sx: = X1; sxmove: = 3; {начальная точка движения и} Sy: = Y1; symove: = - 1;{шаг перемещения шарика} SetFillStyle ( SolidFill , Red );{выбор типа заливки} PieSlice ( X1 + R, Y1 + R, 0, 360, R ); {рисование шарика} Size: = ImageSize (X1, Y1, X2, Y2); {фрагмент в байтах} GetMem (P, Size); {размещение буфера} GetImage (X1, Y1, X2, Y2, P^); {фрагмент заносится в буфер} SetFillStyle (CloseDotFill, blue); {тип заливки фона} Bar (50, 50, maxx – 50, maxx –50); {фоновая картинка} Repeat {наличие движения шарика} PutImage (Sx, Sy, P^, XORPut); {ввод шарика} Delay (12); {пауза} PutImage (Sx, Sy, P^, XORPut); {стирание шарика} {ограничение на движение шарика в пределах поля фона } If (Sx < 50) or (Ss >maxx – 50 –2 * R) then sxmove: = - sxmove; If (Sy < 50) or (Sy > maxy – 50 –2 * R) then symove: = - symove; Inc (Sx, sxmove); {следующая точка появления} Inc {Sy, symove); {шарика на экране} Until KeyPressed; {…пока не нажата клавиша} FreeMem (P, Size); {освобождение памяти буфера} CloseGraph; {закрытие режима графики} END. В приведенном примере продемонстрирован алгоритм мультипликации, применяющий “битовые” методы (используется пересылка битовых массивов процедурами GetImage и PutImage).Скорость движения картинки сильно зависит от разрешения экрана и количества цветов: наибольшая – в режиме CGA 640x200 2 цвета (1 бит на пиксел), наименьшая – в режиме VGA 320x200 256 цветов (8 бит на пиксел). 43 Возникают также некоторые сложности с синхронизацией перемещений и частоты вертикальной развертки монитора. Обращаем внимание на стандартную связку: Size: =ImageSize (X1, Y1, X2, Y2); {фрагмент в байтах} GetMem (P, Size); {размещение буфера} GetImage (X1, Y1, X2, Y2, P^); {фрагмент помещается в буфер} .. PutImage( X, Y, P^, xxxPut); {вывод фрагмента( ов) } FreeMem (P, Size); {освобождение буфера} организующую хранение динамического фрагмента через переменную P типа Pointer (хранит адрес памяти – «кучи» при выполнении программы, т.е. является указателем на нее). В вызовах PutImage / GetImage указатель P должен быть разыменован (т.е. обращаться не к адресу, а к самой памяти или хранящемуся там значению). Таким образом, здесь используется динамическая область памяти – «куча», выделяемая программе при ее выполнении. 44