Турбо-Паскаль 7.0
Необходимые сведения о компьютере и программе. Командный и программный режимы программы "Турбо-Паскаль 7.0". Простые, линейные программы. Операторы ввода-вывода. Запись арифметических выражений. Стандартный модуль Graph, текстовый и графический режимы.
Рубрика | Программирование, компьютеры и кибернетика |
Вид | учебное пособие |
Язык | русский |
Дата добавления | 13.10.2012 |
Размер файла | 1,5 M |
Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже
Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.
и с ними можно выполнять арифметические действия}
summa:= dlina[1]+dlina[2]+dlina[3];
WriteLn(summa)
END.
Если смысл написанного выше вам неясен, запустите отладочный пошаговый режим выполнения программы, заставив Паскаль показывать вам текущие значения dlina[1], dlina[2], dlina[3], summa.
Теперь запишем ту же программу в предположении, что длины удавов заранее неизвестны и мы их вводим при помощи ReadLn:
VAR dlina :array [1..3] of Integer;
summa :Integer;
BEGIN
ReadLn (dlina[1],dlina[2],dlina[3]);
summa:= dlina[1]+dlina[2]+dlina[3];
WriteLn(summa)
END.
Теперь решим ту же задачу в предположении, что удавов не три, а тысяча:
VAR dlina :array [1..1000] of Integer;
summa, i :Integer;
BEGIN
{Вводим длины тысячи удавов, хоть это и утомительно:}
for i:=1 to 1000 do ReadLn (dlina[i]);
{Здесь на первом выполнении цикла i=1 и поэтому компьютер выполняет ReadLn(dlina[1]),
на втором - i=2 и поэтому компьютер выполняет ReadLn(dlina[2]) и т.д.}
{Определяем суммарную длину тысячи удавов:}
summa:= 0;
for i:=1 to 1000 do summa:=summa+dlina[i]);
WriteLn(summa)
END.
Решим еще одну задачу. Дан ряд из 10 произвольных чисел: a[1], a[2], ... , a[10]. Подсчитать и напечатать суммы троек стоящих рядом чисел: a[1]+a[2]+a[3], a[2]+a[3]+a[4], a[3]+a[4]+a[5], ...... , a[8]+a[9]+a[10].
VAR a :array [1..10] of Integer;
i :Integer;
BEGIN
for i:=1 to 10 do ReadLn (a[i]);
for i:=1 to 8 do WriteLn ( a[i]+ a[i+1]+ a[i+2] )
END.
Задание 103:. Напишите программу вычисления среднегодовой температуры (Для проверки в компьютере годом можно считать неделю).
Задание 104:. Подсчитайте количество морозных дней (когда температура ниже -20 град.).
Задание 105:. Каким по порядку идет самый морозный день?
Задание 106:. Вычислить и распечатать первые тридцать чисел Фибоначчи.
3.2.3.3 Двумерные массивы
Поясним суть двумерных массивов на простом примере. Пусть на целом ряде метеостанций, расположенных в разных точках земного шара, в течение многих дней измеряли температуру воздуха. Показания термометров свели в таблицу. Ограничимся для экономии места тремя станциями и четырьмя днями.
1-й день |
2-й день |
3-й день |
4-й день |
||
Метеостанция 1 |
-8 |
-14 |
-19 |
-18 |
|
Метеостанция 2 |
25 |
28 |
26 |
20 |
|
Метеостанция 3 |
11 |
18 |
20 |
25 |
Требуется:
Распечатать показания термометров всех метеостанций за 2-й день
Определить среднюю температуру на третьей метеостанции
Распечатать всю таблицу
Распечатать, в какие дни и на каких метеостанциях температура была в диапазоне 24-26 градусов тепла
Для этого обозначим показания термометров индексированной переменной с двумя индексами по следующей схеме:
t[1,1] t[1,2] t[1,3] t[1,4]
t[2,1] t[2,2] t[2,3] t[2,4]
t[3,1] t[3,2] t[3,3] t[3,4]
Обратите внимание, что первый индекс в скобках обозначает номер строки (метеостанции), второй - номер столбца (дня) прямоугольной таблицы.
Программа:
{В памяти отводим массив из 3*4=12 ячеек под значения типа Integer индексированной переменной t. Будем называть его двумерным массивом:}
VAR t :array [1..3, 1..4] of Integer;
s,i,j :Integer;
BEGIN {Зададим значения элементов массива примитивным присваиванием:}
t[1,1]:=-8; t[1,2]:=-14; t[1,3]:=-19; t[1,4]:=-18;
t[2,1]:=25; t[2,2]:= 28; t[2,3]:= 26; t[2,4]:= 20;
t[3,1]:=11; t[3,2]:= 18; t[3,3]:= 20; t[3,4]:= 25;
{А теперь распечатаем второй столбец массива:}
for i:=1 to 3 do WriteLn(t[i,2]);
{Определим среднее значение элементов третьей строки:}
i:=3;
s:=0;
for j:=1 to 4 do s:=s+t[i,j];
WriteLn(s/4 :10:3);
{Распечатаем всю таблицу:}
for i:=1 to 3 do for j:=1 to 4 do WriteLn (t[i,j]);
{Распечатаем станции и дни с температурой 24-26 градусов:}
for i:=1 to 3 do for j:=1 to 4 do
if (t[i,j]>=24) AND (t[i,j]<=26) then WriteLn ('Станция ',i,' день ',j)
END.
Задание 107: Вычислить разницу между максимальной и минимальной температурой во всей таблице.
3.2.3.4Какие бывают массивы
Массивы могут быть одномерные, двумерные, трехмерные, четырехмерные и т.д.:
array [1..10] of Integer -одномерный массив 10 ячеек
array [1..10, 1..5] of Integer -двумерный массив 50 ячеек
array [1..10, 1..5, 1..2] of Integer -трехмерный массив 100 ячеек
array [1..10, 1..5, 1..2, 1..3] of Integer -четырехмерный массив300 ячеек
Массивы бывают не только числовые, но и символьные, строковые и прочие. Подходит любой известный нам тип. Например:
array [1..50] of Char
Это означает, что в каждой из 50 ячеек должно находиться не число, а произвольный символ. Еще один пример:
array [1..50] of String
Здесь в каждой из 50 ячеек должна находиться строка. Примеры программ с такими массивами мы увидим в 12.13.
Границы индексов в квадратных скобках тоже могут быть разными, например:
array [20..60] of Real
Здесь под вещественные числа отводится 41 ячейка.
array [0..9, -10..30] of Real
Здесь под вещественные числа отводится 10*41=410 ячеек.
Вообще индексы могут быть не только числовыми, но и любыми порядковыми. Например,
array [`А'..'Я'] of Real
Зачем это нужно, будет ясно в 12.8.
Полная синтаксическая информация о массивах будет приведена в 14.8.
Какая польза от массивов при программировании игр? Вряд ли хоть одну «умную» игру можно запрограммировать без применения массивов. Возьмем хотя бы «крестики-нолики» на поле 3 на 3. Вам придется рисовать на экране большие клетки, а в них - нолики (кружочки) после ваших ходов и крестики (пересекающиеся линии) после ходов компьютера. Но этого недостаточно. Чтобы компьютер мог поставить крестик в свободном поле, он должен хотя бы знать, а в каких клетках крестики и нолики уже стоят. Анализировать для этого информацию о пикселах экрана очень неудобно. Гораздо разумнее заранее организовать VAR a: array[1..3,1..3] of Byte и записывать туда в нужные места нолики после ходов человека и единички после ходов компьютера. Сразу же после записи в массив 0 или 1 программа должна рисовать в соответствующем месте экрана кружок или крестик. Мыслить компьютер мог бы при помощи примерно таких операторов - if (a[1,1]=0) AND (a[1,2]=0) then a[1,3]:=1. Это очевидный защитный ход компьютера.
3.2.4 Определения констант
Приведем программу вычисления среднегодовой температуры для задания 1 из 12.3.
VAR s,i:Integer; t:array [1..365] of Integer;
BEGIN
for i:=1 to 365 do ReadLn(t[i]);
s:=0;
for i:=1 to 365 do s:=s+t[i];
WriteLn(s/365)
END.
Пусть нам потребовалось переделать эту программу на вычисление средней недельной температуры. Для этого достаточно везде в тексте программы число (константу) 365 заменить на число (константу) 7. В больших программах одна и та же константа может встречаться десятки раз и подобный процесс может потребовать значительного расхода времени. В 10.4 мы уже сталкивались с такой ситуацией. Там мы нашли выход в том, что вместо константы записывали переменную величину. Но в нашем случае этого не получится, так как Паскаль запрещает задавать границу в описании массива переменной величиной. В таких случаях поступают следующим образом. Константе придумывают имя (как переменной), например k, и в специальном разделе const ей задается значение. Вот наша программа с использованием константы k:
CONST k =365; {Обратите внимание, что в определении вместо := стоит = }
VAR s,i :Integer;
t :array [1..k] of Integer;
BEGIN
for i:=1 to k do ReadLn(t[i]);
s:=0;
for i:=1 to k do s:=s+t[i];
WriteLn(s/k)
END.
В приведенном виде программа стала универсальной. Теперь для ее переделки под средненедельную температуру достаточно в одном месте поменять определение k=365 на k=7.
Значению константы запрещено меняться в процессе выполнения программы, то есть запрещены операторы вида k:=30 и ReadLn(k). Паскаль присматривает за этим.
Тип константы указывать нельзя если это не типизированная константа (речь о них - в следующем параграфе). Паскаль сам догадается о типе по записи:
CONST n =800; {тип целочисленный}
e =2.7; {тип Real}
bukva ='ж'; {тип Char}
Slash ='/'; {тип Char}
slovo ='хорошо'; {тип String}
OK =true; {тип Boolean}
Имя константы образуется из букв и цифр так же, как и имя переменной. Важное отличие константы от переменной в том, что значение переменной задается на этапе выполнения программы, а значение константы - раньше, на этапе компиляции. Рекомендую вам там, где можно, вместо переменных применять константы. Программа получается строже.
3.2.5 Типизированные константы
В блоке CONST можно описывать не только константы, но и переменные величины. Эти переменные величины из-за того, что они описаны в таком странном месте, приобретают неудачное название типизированные константы, но переменными быть не перестают, а самое для нас главное - здесь им можно удобно придавать начальные значения. Вот пример:
CONST n :Integer =800;
e :Real =2.7;
Вот как запишется программа для вычисления средней недельной температуры из 12.4, если массив t описать как типизированную константу:
CONST k =7; { k - обычная, нетипизированная константа}
t :array [1..k] of Integer = (-14, -12, -8, -2, 0, 1, -3);
{ t - типизированная константа}
VAR s,i :Integer;
BEGIN
s:=0;
for i:=1 to k do s:=s+t[i];
WriteLn(s/k)
END.
Здесь в круглых скобках задается список начальных значений переменной t, а именно: t[1] равно -14, t[2] равно -12 и т.д. В разделе операторов эти значения можно менять.
Двумерным массивам начальные значения придаются аналогично. Так в программе из 12.3 вместо двенадцати присвоений можно было записать так:
Const k =3; n=4;
t :array [1..k,1..n] of Integer = (( -8,-14,-19,-18),
( 25, 28, 26, 20),
( 11, 18, 20, 25));
.......
Обратите внимание на расстановку скобок.
3.2.6 Придумываем типы данных
Паскаль предоставляет возможность не только пользоваться стандартными типами данных, но также именовать их по-другому и даже создавать свои типы.
Запись TYPE bukva = Char
означает: ТИП bukva "равен" (эквивалентен) типу Char,
то есть мы просто придумали типу Char еще одно название "bukva". Теперь все равно, как записать:
VAR a,b:Char
или VAR a,b:bukva .
Еще примеры: type Vector = array[1..10] of Integer;
matritsa = array[1..8] of Vector;
var a,b :Vector;
c :matritsa;
d :array[1.. 8] of Vector;
Здесь мы создали два новых типа с именами Vector и matritsa. Очевидно, переменные c и d описаны одинаково. Обратите внимание, что вместо type matritsa = array[1.. 8] of Vector
можно записать type matritsa = array[1.. 8] of array[1..10] of Integer
или type matritsa = array[1..8,1..10] of Integer .
Зачем нужны новые типы? Вот две из нескольких причин. Одна - наглядность и удобство. Другая - чисто грамматическая - Паскаль разрешает в определенных конструкциях записывать лишь имена типов, а не их определения. Например, когда мы изучим процедуры с параметрами, мы узнаем, что
писать procedure p(a: array[1..10] of Integer) неправильно,
а писать procedure p(a: Vector) правильно.
3.2.7 Логический тип Boolean
В операторах if, while, repeat мы привыкли писать выражения вида a>b, i<=0, c='крот', 3>2, (a>b)AND(a>c) и т.п. Про каждое из этих выражений можно сказать, истинно оно в данный момент или ложно. Например, выражение 3>2 истинно всегда, а выражение i<=0 ложно в тот момент, когда i равно, скажем, 2. Такие выражения называются логическими выражениями.
Говорят, что логическое выражение 3>2 имеет значение “истина” (по-английски true - “тру”), а логическое выражение i<=0 имеет значение “ложь” (по-английски false - “фолс”).
Внутренняя идеология построения языка Паскаль требует определить новый тип переменных - логический тип Boolean. Запись VAR a:Boolean означает, что переменная a может принимать всего два значения - true и false. Так, мы можем записать a:=false.
Слова true и false являются логическими константами и их можно употреблять в логических выражениях или вместо них. Например, if a=true then... Конструкцию if (a>b)=false then... можно перевести “если неправда, что a больше b, то...”.
Значения true и false удобно применять для организации бесконечных циклов:
while true do .......
repeat ........... until false
Решим конкретный пример на этот тип.
Задача: В группе - 6 студентов. Сколько из них сдали зачет по физике?
Сначала напишем программу без использования типа Boolean. В ней единицей я обозначил зачет, нулем - незачет. Массив Zachet из 6 элементов хранит информацию о зачете.
CONST Zachet :array[1..6] of Integer = (1,1,0,1,1,1);
VAR c,i :Integer;
BEGIN
c:=0; {c - счетчик зачетов}
for i:=1 to 6 do if zachet[i] = 1 then c:=c+1;
WriteLn(c)
END.
Теперь напишем программу с использованием типа Boolean. В ней через true я обозначил зачет, через false - незачет.
CONST Zachet :array[1..6] of Boolean = (true,true, false, true, true, true);
VAR c,i :Integer;
BEGIN
c:=0;
for i:=1 to 6 do if zachet[i] = true then c:=c+1;
WriteLn(c)
END.
Отличие второй программы от первой в том, что выражение zachet[i] = true (зачет равен истина) выглядит естественнее и понятнее, чем zachet[i] = 1 (зачет равен единице, то есть колу?). В общем, чуть-чуть нагляднее.
Кстати, вполне правильно было бы написать и if zachet[i] then .... Ведь условием после слова if может стоять любое логическое выражение, имеющее значением true или false.
3.2.8 Перечислимые типы
В 5.7 я говорил о порядковых типах - это те типы, все значения которых можно выстроить по порядку и перечислить от начала до конца. Мы пока знаем, что в Паскале порядковыми типами являются целочисленные типы, символьный тип и логический тип. Кроме того, программист может придумывать собственные порядковые типы. Рассмотрим, например, такую конструкцию:
VAR Month : (january, february, march, april, may, june, july, august, september, october, november, december)
Она означает, что переменная Month может принимать только одно из перечисленных в скобках значений. Например, можно записать Month:= may. Переменная Month является переменной перечислимого типа, который является одним из видов порядковых типов.
Эти значения ни в коем случае не являются строками. Так, нельзя записать Month:= `may'. Кроме того, их нельзя вывести на печать, вообще они не могут быть введены в компьютер или выведены из него, например, при помощи операторов Read и Write. Однако, их удобно применять при программировании. Это удобство выяснится из следующего примера.
Задача: Известно, сколько дней в каждом месяце года. Сколько дней летом?
Сначала запишем программу традиционным способом.
Программа:
CONST dni :array[1..12] of Byte = (31,28,31,30,31,30,31,31,30,31,30,31);
VAR s,i :Integer;
Begin
s:=0; {Сумматор летних дней}
for i:=6 to 8 do s:=s+dni[i]; {Летние месяцы - 6,7,8}
WriteLn(s)
end.
Недостаток приведенной программы - не самая лучшая наглядность, к тому же приходится самому на пальцах вычислять номера месяцев начала и конца лета (6 и 8). Паскаль имеет средства повысить наглядность и удобство таких программ. Запишем нашу программу по-новому, с использованием перечислимого типа данных:
TYPE mes = (january, february, march, april, may, june, july, august, september,
october, november, december);
CONST dni :array[january..december] of Byte =
(31,28,31,30,31,30,31,31,30,31,30,31);
VAR s :Integer;
i :mes;
begin
s:=0;
for i:=june to august do s:=s+dni[i];
WriteLn(s)
end.
Пояснения: Основное достижение нашей программы в том, что в операторе for можно написать june to august вместо 6 to 8, а в определении массива dni можно написать array[january..december] вместо array[1..12]. Для этого пришлось определить специальный перечислимый тип mes, перечислив в скобках произвольные имена месяцев, а переменную цикла i задать типом mes, а не Integer.
Синтаксис перечислимого типа:
(имя , имя , имя , . . . . , имя)
Значения перечислимого типа можно использовать так же свободно, как и значения порядковых типов, например:
if i = february then dni[i]:= 29
3.2.9 Ограниченный тип (диапазон)
Задача: Поезд отправляется в путь в 22 часа и находится в пути 10 часов. Во сколько он прибывает на место назначения?
Ошибочная программа:
VAR Otpravlenie, Pribitie :Byte;
BEGIN
Otpravlenie:=22;
Pribitie:=Otpravlenie+10;
WriteLn(Pribitie)
END.
Эта программа вместо ответа “8” напечатает ответ “32” и ошибки не заметит. Паскаль не знает, что имеют смысл только те значения переменной Pribitie, которые находятся в диапазоне от 0 до 24. Это должен был знать программист, но он тоже не обратил на это внимания. Хотелось бы, чтобы Паскаль вместо выдачи неправильного ответа напоминал забывчивым программистам, что переменная вышла из имеющего смысл диапазона. Для этого программист должен иметь возможность этот диапазон Паскалю указать. Такую возможность дает применение диапазонов (ограниченных типов).
Вот программа, обнаруживающая собственную ошибку:
VAR Otpravlenie, Pribitie : 0..24;
BEGIN
Otpravlenie:=22;
Pribitie:=Otpravlenie+10;
WriteLn(Pribitie)
END.
Конструкция VAR Otpravlenie, Pribitie : 0..24 означает, что переменные Otpravlenie и Pribitie имеют право принимать значения целых чисел в диапазоне от 0 до 24.
Паскаль будет обнаруживать выход за диапазон только в том случае, когда вы установите флажок (крестик) в OptionsCompiler…Compiler OptionsRuntime Errors в положение Range Checking (см. часть IV - «Обзор популярных команд меню»).
Диапазон можно задавать для любого порядкового типа, например:
VAR Month :(january, february, march, april, may, june, july, august, september, october, november, december);
Spring :march..may;
Autumn :september..november;
tsifra :0..9;
Zaglavnie:'А'..'Я'
Диапазон является одним из видов порядковых типов.
Задание 108: Известны дата и время (месяц, день, час, минута) отплытия теплохода летом этого года из Москвы в Астрахань. Известно время в пути (в днях, часах и минутах). Оно не превышает 20 суток. Определить дату и время прибытия теплохода в Астрахань. Использовать диапазоны.
Вариант 1: Для простоты предположим, что путешествие начинается между 1 и 10 июня.
Вариант 2. Путешествие начинается в любой день лета. Определите еще и дни недели отправления и прибытия, если известно, какой день недели был 1 июня.
3.2.10Действия над порядковыми типами
Напомню, что порядковыми типами данных в Паскале называются следующие типы: целочисленные типы, Boolean, Char, перечислимый тип и диапазон.
Как видите, сюда не входят вещественные типы и String.
Порядковый тип - это такой тип, все значения которого можно перечислить, посчитать с начала до конца. Например, в тип Byte входит всего 256 различных значений, а именно все целые числа от 0 до 255. В тип Integer входит 65536 значений - целые числа от -32768 до 32767. Тип Char тоже порядковый, так как количество различных символов в Паскале ограничено числом 256.
Любой порядковый тип имеет внутреннюю нумерацию. Пусть мы задали тип TYPE weekday = (mon,tu,we,th,fr) . Внутри компьютера mon будет иметь номер 0, tu - номер 1, we - 2, th - 3, fr - 4. Пусть мы задали переменную VAR a: array[mon..fr] of Real . Теперь для компьютера запись a[we] означает то же, что и запись a[2], если заранее была бы задана переменная VAR a: array[0..4] of Real .
Тип Char имеет нумерацию от 0 до 255. Внутренний номер символа есть его код по таблице кодировки ASCII. Например, буква Б имеет номер (код) 129.
У целочисленных типов (Byte, ShortInt, Word, Integer, LongInt) внутренний номер совпадает с самим числом. Так, число -58 в типе ShortInt имеет номер -58.
Внутренний номер элемента диапазона равен внутреннему номеру элемента типа, для которого создан диапазон. Пусть мы для типа weekday создали диапазон TYPE days = we .. fr. Здесь we будет иметь номер 2, а не 0.
Операции над порядковыми типами:
1. ORD. Эта функция выдает (или, как еще говорят - возвращает) внутренний номер значения любого порядкового типа. Например:
Ord('Б') возвращает 129
Ord (we) возвращает 2
Ord(-58) возвращает -58
2. В любом порядковом типе выполняются операции сравнения > < >= <= = <>. Например, справедливы неравенства 'ю' < 'я' , we > tu . Это возможно потому, что операции сравнения выполняются фактически не над самими значениями, а над их внутренними номерами.
3. SUCC - сокращение от successor (следующий по порядку). Эта функция возвращает следующий по порядку элемент любого порядкового типа. Например:
Succ (8) возвращает 9
Succ('Ю') возвращает 'Я'
Succ (we) возвращает th
4. PRED - сокращение от predecessor - это successor "наоборот". PRED возвращает предыдущий элемент любого порядкового типа. Например:
Pred (25) возвращает 24
Pred('д') возвращает 'г'
Pred (tu) возвращает mo
Эти функции, как и любые другие, можно применять в выражениях. Например, оператор y:=10+Ord(we)+Succ(8) присвоит переменной y значение 21.
В операторе for переменная цикла может быть любого порядкового типа, например:
for ch := 'd' to 'h' do ...
Задание 109: Подсчитать, сколько заглавных букв в диапазоне от Б до Ф.
Задание 110: Правда ли, что сентябрь наступает позже июля?
Задание 111: В кондитерском магазине стоит очередь за Сникерсами. В очереди - Nina, Olga, Alex, Marianna, Ester, Misha, Tolik, Lena, Oleg, Anton, Pankrat, Robocop, Dima, Donatello, Zina, Sveta, Artur, Ramona, Vera, Igor, Ira. Известно, сколько у каждого денег. Спрашивается:
Хватит ли у них всех вместе денег на Сникерс (3 рубля) ?
Какой по порядку в очереди стоит Лена?
Правда ли, что у Панкрата денег больше, чем у Миши?
Указание: для хранения денег организовать массив.
3.2.11Символьный тип Char. Работа с символами
С символьным типом Char мы познакомились в 5.6. Значением символьной переменной являются символы из таблицы ASCII.
Для работы с символами вам достаточно кроме вышеизложенных знать еще одну функцию - Chr. Выполнив оператор c1:=Chr(69), Паскаль присваивает c1 значение символа, соответствующего номеру 69 по таблице ASCII, т.е. латинского 'Е'.
Задание 112: Угадайте, что напечатает компьютер, выполнив оператор Write(Chr(Ord(Succ(Pred('+')))))
Задание 113: Распечатайте часть таблицы ASCII, конкретнее - символы, соответствующие кодам 32-255. Обратите внимание:
на символы так называемой псевдографики, применяемые для вычерчивания таблиц в текстовом режиме;
на разрыв в расположении строчных букв русского алфавита.
Использование клавиш передвижения курсора для управления компьютером с клавиатуры.
В 10.7 мы с вами научились вмешиваться в работу программы нажатием алфавитных и цифровых клавиш. Например, мы можем записать
if ReadKey='R' then…,
подразумевая какие-либо действия в случае, если нажата клавиша R. Если вы знаете коды клавиш по таблице ASCII, то вы можете то же самое записать по другому:
if ReadKey=#82 then…,
так как код клавиши R равен 82. Этот способ более универсальный, так как коды в буфер клавиатуры посылают и те клавиши клавиатуры, которым не приписано никакого символа. Например, клавиша Tab посылает код 9.
Итак, нам хотелось бы для управления компьютером использовать и другие клавиши, например, ?. Сложность в том, что в отличие от алфавитных и цифровых клавиш, эти и некоторые другие клавиши и комбинации клавиш посылают в буфер клавиатуры не один код, а два, причем первый из них - ноль. Например, клавиша посылает в буфер пару (0,72), клавиша Insert посылает в буфер пару (0,82). Эта парочка называется расширенным кодом. Вот что будет в буфере, если мы нажмем подряд шесть клавиш: R R R Insert Insert:
82 |
82 |
82 |
0 |
72 |
0 |
82 |
0 |
82 |
Если вы помните механику работы буфера клавиатуры, то можете умелым использованием функции ReadKey выудить факт нажатия нужной вам клавиши. Так, если вы хотите определить, была ли нажата Insert, то можете записать такой фрагмент:
kl:= ReadKey; if kl =#0 then if ReadKey=#82 then…
При этом компьютер не спутает невинную клавишу R с клавишей Insert.
Вот коды, которые посылают некоторые клавиши в буфер клавиатуры:
0 72 |
0 80 |
0 75 |
0 77 |
|||||
Page Up |
0 73 |
Page Down |
0 81 |
Home |
0 71 |
End |
0 79 |
|
Insert |
0 82 |
Delete |
0 83 |
BackSpace |
8 |
Esc |
27 |
|
Tab |
9 |
Enter |
13 |
пробел |
32 |
серый + |
43 |
|
F1 |
0 59 |
F2 |
0 60 |
F3 |
0 61 |
F4 |
0 62 |
|
F5 |
0 63 |
F6 |
0 64 |
F7 |
0 65 |
F8 |
0 66 |
|
F9 |
0 67 |
F10 |
0 68 |
F11 |
0 133 |
F12 |
1. 134 |
3.2.12 Строковый тип String. Работа со строками
Со строковым типом String мы познакомились в 4.14.
Как можно сэкономить память, работая со строками? Если мы напишем VAR a:String, то Паскаль отведет под символы строковой переменной a 255 байтов. Если мы не собираемся присваивать переменной b значений длиннее, например, 20 байтов, то выгодно написать VAR b:String[20]. В этом случае под символы переменной b в памяти будет отведено 20 байтов.
Теперь разберем функции для работы над строками.
Исходные данные |
Операция |
Результат |
Пояснение |
|
s1:='Мото'; s2:='роллер' |
s3:=s1+s2 |
s3='Мото-роллер' |
Операция + над двумя строками просто соединяет две строки в одну |
|
s5:='Мото-роллер' |
k:=Pos('рол',s5) |
k=5 |
Функция Pos возвращает позицию, на которой находится строка 'рол' в строке s5 |
|
s3:='Мото-роллер' |
l:=Length(s3) |
l=10 |
Функция Length (длина) выдает (возвращает) количество символов в строк |
|
s3:='астро-ном' |
s4:=Copy(s3,3,4) |
s4= `трон' |
Функция Copy возвращает часть строки длиной 4, начиная с третьего символа |
|
s5:='Коро-бочка'; |
Delete(s5,4,2) |
s5='Коро-чка' |
Процедура Delete удаляет из строки s5 два символа, начиная с четвертого |
|
s6:='Рука'; s7:='баш'; |
Insert(s7,s6,3) |
s6='Руба-шка' |
Процедура Insert вставляет в строку s6 строку s7, начиная с третьего символа |
|
x:=2.73284 |
Str(x:4:2,s8) |
s8='2.73' |
Процедура Str преобразует число в строку. 4:2 - это желаемый формат числа (см. 14.5) |
|
s8='2.73' |
Val(s8,x,Osh) |
x=2.73 |
Процедура Val преобразует строку в число. Параметр Osh должен иметь тип Integer. Он имеет смысл при анализе ошибки в преобразовании |
Процедура Str может вам понадобиться, например, вот в каком случае. Модуль Graph имеет возможность печатать на экране большими красивыми шрифтами (см. 15.6). Но так печатает он только строковый тип. А в программе “Торпедная атака” вам может захотеться печатать красивым шрифтом счетчик подбитых кораблей, который у вас описан, как целочисленный. Вот тут и пригодится Str. Примеры использования Str и Val см.в 15.6.
Если задана строка s:='Банка', то считается автоматически заданным массив символов с тем же именем: s[1]='Б', s[2]='а', s[3]='н', s[4]='к', s[5]='а'. Тогда после выполнения оператора s[3]:='р' мы получим s='Барка'.
Строки можно сравнивать. Условие s1=s2 считается выполненным, если обе строки абсолютно одинаковы, включая и пробелы. Сравнение идет посимвольно слева направо. Поэтому считается, что `панк' < `парк', так как первый несовпадающий символ `р' имеет больший номер, чем `н'.
Задание 114: Среди детей встречается игра, заключающаяся в зашифровке своей речи “для секретности” за счет вставки в произносимые слова какого-нибудь словосочетания, например, “быр”. Тогда вместо слова “корова” будет произнесено “кобырробырвабыр”. Составьте программу, которая распечатывает заданную строку, после каждой второй буквы вставляя “быр”.
Задание 115: Давайте поставим задачу шифрования текста более серьезно. Имеется строка текста. Требуется написать программу, которая зашифровывала бы ее в другую строку. Способов шифровки вы можете придумать сколько угодно. Попробуйте такой - заменять каждый символ текста символом, следующим по порядку в таблице ASCII. Тогда слово КОТ превратится в слово ЛПУ. Составьте, пожалуйста, и программу дешифровки. Когда вы познакомитесь с файлами, вы сможете уже зашифровывать и дешифровывать не отдельные строки, а целые тексты. В том числе и ваши паскалевские программы.
Записи
На вооружении флота 100 подводных лодок. Адмиралу часто приходится решать задачи такого типа: 1)перечислить названия лодок, имеющих скорость, превышающую скорость вражеской подводной лодки Шредер; 2)подсчитать, сколько лодок имеют на вооружении больше 10 торпед; и т.д. Чтобы ускорить решение таких задач, адмирал приказал занести в компьютер сведения обо всех лодках, включая вражеские лодки Шредер и Рокстеди, а именно: их названия, скорость и число торпед, находящихся на вооружении.
Отвести место в оперативной памяти под указанную информацию о 102 лодках можно двумя способами: 1)с помощью массивов, 2)с помощью записей.
Рассмотрим программу, использующую первый способ. В каждом массиве будет 102 элемента, причем элементы 101 и 102 отведены под лодки противника.
VAR nazvanie :array[1..102] of String; {Место под 102 названия}
skorost :array[1..102] of Real; {Место под 102 скорости}
torped :array[1..102] of Byte; {Место под 102 количества торпед}
i :Integer;
BEGIN
{Здесь каким-нибудь способом заносим в отведенное место всю информацию,
например, присвоением - nazvanie[1]:='Щука'.... или загрузкой из файла}
{А теперь решим первую из двух задач:}
for i:=1 to 100 do if skorost[i] > skorost [101] then WriteLn(nazvanie[i])
END.
В памяти компьютера информация размещается в том порядке, в котором она встречается в описаниях:
ЯЧЕЙКИ ДЛЯ ИНФОРМАЦИИ |
ИНФОРМАЦИЯ |
|
nazvanie[1] |
Щука |
|
nazvanie[2] |
Дельфин |
|
........... |
........ |
|
nazvanie[101] |
Шредер |
|
nazvanie[102] |
Рокстеди |
|
skorost[1] |
26 |
|
skorost[2] |
24 |
|
........... |
........ |
|
skorost[101] |
20 |
|
skorost[102] |
18 |
|
torped[1] |
6 |
|
torped[2] |
10 |
|
........... |
........ |
|
torped[101] |
15 |
|
torped[102] |
22 |
|
i |
? |
Вы видите, что данные об одной лодке разбросаны по памяти.
Рассмотрим второй способ. Иногда бывает удобно, чтобы данные, касающиеся одной лодки, хранились в памяти рядом, вот так:
ЯЧЕЙКИ ДЛЯ ИНФОРМАЦИИ |
ИНФОРМАЦИЯ |
|
lodka[1].nazvanie |
Щука |
|
lodka[1].skorost |
26 |
|
lodka[1].torped |
6 |
|
lodka[2].nazvanie |
Дельфин |
|
lodka[2].skorost |
14 |
|
lodka[2].torped |
10 |
|
. . . . . . . . . . . . . . |
. . . . . . . . . |
|
vr .nazvanie |
Шредер |
|
vr .skorost |
20 |
|
vr .torped |
15 |
|
prot .nazvanie |
Рокстеди |
|
prot .skorost |
18 |
|
prot .torped |
22 |
Выстроенную подобным образом информацию в памяти компьютера часто называют базой данных.
Сами по себе массивы не позволяют хранить информацию в таком порядке, для этого нужно использовать записи. Запись - это набор данных (полей) различных типов, касающийся одного объекта. Например, запись, касающаяся нашей первой лодки, это набор трех полей: название - Щука (тип String), скорость - 26 (тип Real), количество торпед - 6 (тип Byte). Точка отделяет имя поля от обозначения записи, содержащей это поле.
Напомним, что в массиве разрешается хранить данные только одного типа.
Прежде чем отводить место в памяти под всю информацию, объясним Паскалю, из чего состоит одна запись, то есть опишем ее, задав специальный тип записи record и придумав ему имя, скажем, podlodka:
TYPE podlodka = record
nazvanie :String;
skorost :Real;
torped :Byte;
end;
Тип определен, но место в памяти пока не отведено. Здесь нам, хочешь-не хочешь, придется воспользоваться массивом. При помощи VAR отведем место под массив из 100 записей для наших подлодок и отдельное место под две записи для вражеских. Массиву придумаем имя lodka.
VAR lodka :array[1..100] of podlodka;
vr,prot :podlodka; {Записи для двух вражеских лодок}
i :Integer;
..........................
Как видите, элементами массива могут быть не только отдельные числа, символы или строки, но и такие сложные образования, как записи.
Вот программа целиком:
TYPE podlodka = record
nazvanie :String;
skorost :Real;
torped :Byte;
end;
VAR lodka :array[1..100] of podlodka;
vr,prot :podlodka;
i :Integer;
BEGIN
{Здесь задаем значения полям всех записей. Конечно, удобнее это делать при помощи типизированных констант (см.следующую программу) или файлов данных, но я использую простое присвоение:}
lodka[1].nazvanie :='Щука';
lodka[1].skorost :=26;
. . . . . . . .
prot.torped :=22;
{А теперь решаем первую задачу:}
for i:=1 to 100 do if lodka[i].skorost > vr.skorost then WriteLn (lodka[i].nazvanie)
END.
Согласитесь, что при использовании записей текст программы гораздо понятнее.
Теперь запишем нашу программу с использованием типизированных констант, для краткости ограничив наш флот тремя подводными лодками:
TYPE podlodka = record
nazvanie :String;
skorost :Real;
torped :Byte;
end;
CONST lodka : array[1..3] of podlodka =
((nazvanie:'Щука'; skorost:26; torped: 6),
(nazvanie:'Дельфин'; skorost:14; torped:10),
(nazvanie:'Леонардо'; skorost:28; torped:11));
vr : podlodka =
(nazvanie:'Шредер'; skorost:20; torped:15);
prot : podlodka =
(nazvanie:'Рокстеди'; skorost:18; torped:22);
VAR i : Integer;
BEGIN
for i:=1 to 3 do if lodka[i].skorost > vr.skorost then WriteLn(lodka[i].nazvanie);
END.
Здесь вы видите, как правильно придавать начальные значения типизированным константам типа record.
Задание 116: Создайте базу данных о своих родственниках. О каждом родственнике должно быть известно:
Имя
Год рождения
Цвет глаз
Массивы не используйте. Программа должна:
Распечатать ваш возраст и цвет глаз
Ответить на вопрос - правда ли, что ваш дядя старше тети.
Задание 117: Создайте базу данных о своих однокашниках. О каждом однокашнике должно быть известно:
Фамилия
Имя
Пол
Год рождения
Обязательно используйте массив не меньше, чем из 10 записей. Программа должна:
Вычислить средний возраст ваших однокашников
Определить, кого среди них больше - дам или кавалеров
Ответить на вопрос - есть ли в вашей базе тезки (это нелегко).
Множества
Множеством в Паскале называется набор значений какого-нибудь порядкового типа, подчиняющийся специфическим правилам, о которых мы поговорим дальше. В программе множество записывается в виде списка этих значений в квадратных скобках. Например, [7,5,0,4] или [`п' , 'ж' , 'л']. Множество не должно состоять более, чем из 256 элементов и не должно содержать элементов с порядковыми номерами меньше 0 и больше 255.
Если в множестве элемент повторяется, то считается, что он входит туда только один раз. Например, множества [2,5,2] и [2,5] эквивалентны.
Порядок элементов в множестве не играет роли. Множества [2,5] и [5,2] эквивалентны.
В описании тип множества задается словами set of. Например, конструкция
VAR a : set of Byte
говорит о том, что задана переменная, значением которой может быть любое множество из любого числа элементов типа Byte. Так, в некоторый момент процесса выполнения программы значением a может быть множество [210, 3, 92], а через пару секунд - [8, 5, 3, 26, 17].
Конструкция VAR c: set of (april, may, june) говорит о том, что переменная c может иметь значением любое множество из имен april, may, june. Например, [april, june].
Конструкция VAR d: set of 10..18 говорит о том, что переменная d может иметь значением любое множество целых чисел из диапазона от 10 до 18.
Над множествами определено несколько операций. Рассмотрим три из них: объединение (+), пересечение (*) и разность (-).
Операция |
Результат |
Пояснение |
|
[1,4,4,5] + [1,2,3,4] |
[1,2,3,4,5] |
В результирующее множество входят элементы, имеющиеся хотя бы в одном из исходных множеств |
|
[1,4,4,5] * [1,2,3,4] |
[1,4] |
В результирующее множество входят только те элементы, которые имеются в каждом из исходных множеств |
|
[1,2,3,4] - [1,3,5] |
[2,4] |
В результирующее множество входят те элементы “уменьшаемого”, которые не встречаются в “вычитаемом” |
Операция [1,2]*[3,4] будет иметь результатом [ ], то есть пустое множество.
Вот операции сравнения множеств:
if a = b then ... |
Если множества a и b состоят из одинаковых элементов ... |
|
if a <> b then ... |
Если множества a и b отличаются хотя бы одним элементом ... |
|
if a <= b then ... |
Если a является подмножеством b, то есть все элементы a являются элементами b ... |
|
if a >= b then ... |
Если b является подмножеством a, то есть все элементы b являются элементами a ... |
Операция проверки вхождения элемента E в множество a:
if E in a then ...
Например, a:= [1,2,4]; if 2 in a then ... {Если 2 входит в множество a ....}
К сожалению, Паскаль не желает выводить множества на печать, точно так же, как он не желает печатать перечислимые типы. Поэтому просто так узнать, из каких элементов состоит множество, не удастся. Вот один из обходных путей:
Пусть задано множество a, описанное, как set of Byte. Будем пробовать уменьшать его на все элементы подряд, от 1 до 255, и каждый раз, когда это удается, распечатывать соответствующее число. Вот подходящий фрагмент, в котором мне понадобится “для транзита” еще одно множество b:
for i:=1 to 255 do begin
b:=a-[i];
if a<>b then begin WriteLn(i); a:=b end
end {for}
Вот гораздо более короткий и естественный путь:
for i:=0 to 255 do if i in a then WriteLn(i)
Я думаю, что работа с множествами Паскаля - любопытное и полезное занятие. Например, она нужна математикам, чтобы проверять свои теоремы. Я проиллюстрирую работу с множествами на простеньком примере:
Медиум загадывает шестерку чисел, каждое в диапазоне от 0 до 10 (числа могут и совпадать). Экстрасенс отгадывает их, называя свою шестерку. Есть ли между шестерками совпадающие числа? Если есть, то распечатать их.
Сначала решим задачу традиционными методами, а именно с применением массивов, а не множеств:
CONST razmer = 10; kol = 6;
VAR Medium, Extrasens :array[1..kol] of 0..razmer;
i, j, k :Integer;
BEGIN
{Формируем случайным образом две шестерки:}
Randomize;
for i:= 1 to kol do begin
Medium[i] :=Random(razmer+1);
Extrasens[i] :=Random(razmer+1)
end {for};
{Проверяем две шестерки на совпадение:}
k:=0; {Нам придется подсчитывать количество совпадений. k - счетчик}
for i:= 1 to kol do
for j:= 1 to kol do
if Medium[i] = Extrasens[j] then begin
k:=k+1;
WriteLn(Medium[i]) {Распечатываем совпадения}
end {if};
if k=0 then WriteLn(`Не угадал ни разу`)
END.
У данной программы есть недостатки. Пусть медиум загадал числа 2 4 1 5 4 8, а экстрасенс назвал 1 4 9 6 1 4. Программа распечатает числа 4 4 1 1 4 4, а достаточно было бы только 1 4. К тому же пришлось организовывать счетчик совпадающих чисел, чтобы иметь возможность ответить, угадано ли хоть одно число.
А теперь применяем множества:
CONST razmer = 10; kol = 6;
VAR Medium, Extrasens, a :set of 0..razmer;
i :Integer;
BEGIN
{Формируем случайным образом две шестерки:}
Randomize;
Medium:=[ ]; Extrasens:=[ ]; {Начинаем формировать “с нуля”, то есть с пустых множеств}
for i:= 1 to kol do begin
Medium := Medium + [Random(razmer+1)]; {Наращиваем по одному элементу в множестве медиума}
Extrasens := Extrasens + [Random(razmer+1)] {Наращиваем по одному элементу в множестве экстрасенса}
end {for}
a:= Medium * Extrasens; {Множество a содержит совпадающие числа. Вот так - одним махом.}
if a=[ ] then WriteLn(`Не угадал ни разу`)
else begin
WriteLn(`Есть совпадения, вот они: `);
{Распечатываем элементы множества a:}
for i:=0 to razmer do if i in a then WriteLn(i);
end {else}
END.
Задание 118: Случайным образом формируется небольшое множество заглавных букв русского алфавита. Определить, входит ли в это множество хотя бы одна из букв М,И,Ф.
3.2.13 Расположение информации в оперативной памяти. Адреса
Этот и следующий параграфы носят ознакомительный характер.
Раньше я уподоблял оперативную память тетрадному листу в клеточку. Каждая клетка - байт. Теперь я уподоблю ее многоэтажному небоскребу. Каждый этаж - байт.
Как и положено этажам, байты имеют номера. Эти номера называются адресами. Самый "нижний" байт имеет адрес 0, следующий - 1, следующий - - 2 и т.д. Если память вашего компьютера имеет объем 1 Мегабайт, то вы сами можете вычислить адрес последнего байта, учитывая, что 1 Мегабайт = 1024 Килобайта, a 1Килобайт = 1024 байта. Приняты сокращения: Мегабайт - М, Килобайт - К. Имейте в виду, что во многих книгах адреса записываются не в привычном нам виде, а в так называемой шестнадцатеричной системе счисления.
Во время выполнения вашей программы, написанной на Паскале, в памяти находится самая разная информация. То, что относится к паскалевской программе, располагается "по этажам" в следующем порядке:
байт с адресом 1М-1 |
||
куча |
||
стек |
||
сегмент данных объемом 64К |
||
откомпилированная программа |
||
байт с адресом 0 |
Границы между некоторыми областями памяти не фиксированы и зависят от решаемой задачи и желания программиста. В сегменте данных располагаются переменные, массивы и другие типы данных вашей программы, описанные привычным вам способом в разделах VAR, CONST и т.д. (без использования ссылок). Обратите внимание, что размер сегмента данных весьма невелик (не более 64К). Стек - область памяти, в которой располагаются данные, описанные внутри процедур (этого мы пока не делали, об этом - в Глава 13). Куча - область памяти, в которой располагаются данные, описанные при помощи ссылок.
паскаль турбо программа текстовый
3.2.14 Ссылки
Пусть вы хотите использовать следующий массив:
VAR a: array[1..200, 1..200] of Integer;
Давайте подсчитаем, сколько байтов в памяти займет этот массив. Одно число типа Integer занимает 2 байта. Получаем 200*200*2 = 80000 байтов. В сегменте данных массив не умещается, значит привычным образом работать с ним нельзя. Использование ссылок позволяет разместить его в куче (по английски - heap), имеющей гораздо больший размер.
Я привел лишь один из доводов в пользу применения ссылок. А поближе познакомимся мы со ссылками на простом примере. Задача: Вычислить и напечатать y=a+b, где a и b - целые числа 2 и 3. Вот традиционная программа для решения этой задачи:
VAR a, b, y : Integer;
BEGIN
a:=2; b:=3;
y:=a+b;
WriteLn (y)
END.
А теперь потребуем, чтобы число 2 и результат 5 размещались в куче (впрочем, строго говоря, не обязательно в куче). Вот программа со ссылками:
VAR b : Integer;
a,y : ^Integer;
BEGIN
New(a); New(y);
a^ := 2; b:=3;
y^ := a^ + b;
WriteLn (y^)
END.
Пояснения: Все, что выше BEGIN, выполняется на этапе компиляции: Строка a,y:^Integer приказывает отвести в памяти в сегменте данных две ячейки, но не для будущих чисел 2 и 5, а для адресов ячеек из кучи, в которых эти самые 2 и 5 предполагается хранить. Итак, будущие значения a и y - не числа 2 и 5, а адреса ячеек для этих чисел или, по-другому, ссылки на ячейки для этих чисел. Пока же адреса эти не определены.
Все, что ниже BEGIN, выполняется на этапе выполнения программы: При помощи обращений к процедуре New ( New (a) и New (y) ) мы идем дальше и придаем переменным a и y значения конкретных адресов памяти, то есть отводим для будущих чисел 2 и 5 конкретное место в памяти. Таким образом, мы сталкиваемся с новым для нас явлением - место в памяти отводится не на этапе компиляции, а на этапе выполнения программы. В Паскале имеются средства и освобождать это место на этапе выполнения программы (процедура Dispose, на которой я не буду останавливаться). Называется все это динамическим распределением памяти и сулит выгоды экономным создателям программ, использующим большие объемы разных данных в разные моменты выполнения программы.
Оператор a^:= 2 идет еще дальше и посылает в ячейку, адрес которой находится в ячейке a, число 2. Обозначается такая ячейка - a^. Если бы мне вздумалось написать a:=2, это бы значило, что я хочу послать в ячейку a адрес равный двум, что вполне возможно, но синтаксически неверно, так как численные значения адресов задаются по-другому.
Смысл следующих двух операторов очевиден.
Подведем итог. Значок, поставленный перед типом (например, ^Integer), означает новый ссылочный тип, значения которого обязаны быть адресами переменной (или ссылками на переменную) исходного типа (в нашем случае Integer).
Значок ^, поставленный после переменной ссылочного типа (например, a^), означает переменную, на которую ссылается исходная переменная (в нашем случае исходная переменная a).
Вот еще некоторые возможные операции со ссылками (без особых пояснений):
TYPE D = array[1..10] of Real;
DP = ^D;
Int = ^Integer;
VAR i, j : Int; { i, j - адреса целых чисел}
m : DP; { m - адрес первой ячейки массива из 10 вещ. чисел}
BEGIN
New(i); New(j); New(m);
i^:=4;
j^:=3;
j:=i; {Теперь j и i содержат адреса одного и того же числа - 4}
WriteLn(j^); {поэтому будет напечатано число 4}
m^[9]:=300 {Девятый элемент массива становится равным числу 300}
END.
Вернемся к задаче о размещении большого массива. Поскольку Паскаль вслед за MS-DOS запрещает не только описывать, но также, естественно, и ссылаться на структуру, объемом превышающую 64К, то ссылаться сразу на весь двумерный массив не выйдет и поэтому программа получится непростой:
TYPE a = array[1..200] of Integer;
ap = ^a;
a2 = array[1..200] of ap;
VAR x : a2; {x - массив из 200 адресов (каждый - ссылка на строку из 200 элементов
исходного массива)}
BEGIN
for i:=1 to 200 do New(x[i]); {Место для массива отведено}
............
x[128]^[35]:=800; {Присвоено значение элементу массива}
.............
END.
В заключение упомяну, что ссылки полезны еще тем, что позволяют организовывать в памяти структуры переменной длины, такие как списки, деревья и т.п.
3.3 Процедуры и функции с параметрами
3.3.1 Процедуры с параметрами
Поставим и решим задачу, данную вам в качестве задания в 8.2: Составьте программу с процедурами, которая исполнит мелодию “Чижик-пыжик” (ми-до-ми-до-фа-ми-ре-соль-соль-ля-си-до-до-до).
Воспользуемся нотами третьей октавы:
USES CRT;
PROCEDURE doo; BEGIN Sound(523); Delay(2000); NoSound END;
PROCEDURE re; BEGIN Sound(587); Delay(2000); NoSound END;
PROCEDURE mi; BEGIN Sound(659); Delay(2000); NoSound END;
PROCEDURE fa; BEGIN Sound(698); Delay(2000); NoSound END;
PROCEDURE sol; BEGIN Sound(784); Delay(2000); NoSound END;
PROCEDURE la; BEGIN Sound(880); Delay(2000); NoSound END;
PROCEDURE si; BEGIN Sound(988); Delay(2000); NoSound END;
BEGIN
mi; doo; mi; doo; fa; mi; re; sol; sol; la; si; doo; doo; doo
END.
Все процедуры в нашей программе всегда делают одно и то же. Например, процедура doo всегда издает звук частоты 523 герца и продолжительности 200 мс. Происходит это потому, что в описании процедур мы использовали не переменные величины, а константы.
В реальной музыке ноты принадлежат разным октавам и имеют разную длительность. Чтобы получить ноту четвертой октавы, достаточно умножить частоту одноименной ноты третьей октавы на 2. Например, нота ре четвертой октавы имеет частоту 587*2=1174 гц. Естественно, все ноты четвертой октавы звучат выше нот третьей октавы. Пятая октава получается из четвертой так же, как четвертая из третьей и звучит еще выше. Ноты второй октавы, наоборот, получаются из нот третьей октавы делением частоты на 2.
Поставим задачу - создать более универсальную процедуру. Чтобы заставить ноту звучать по-разному, используем переменные величины. Здесь мы используем ту же самую идею, которую мы использовали в процедуре House из Глава 10, она рисовала дом в разных местах экрана в зависимости от значений переменных величин, задающих его координаты. Для простоты ограничимся пока одной нотой ре и двумя октавами - третьей и четвертой. Длительность пусть будет любая. Пусть программа должна воспроизвести три подряд ноты ре: сначала третья октава одна секунда, затем четвертая октава одна секунда и затем третья октава три секунды.
USES CRT;
VAR octava : Byte;
dlit : Word;
PROCEDURE re; BEGIN
if octava = 3 then Sound(587) else Sound(1174);
Delay(dlit);
NoSound END;
BEGIN
octava:=3; dlit:=1000; re; octava:=4; dlit:=1000; re; octava:=3; dlit:=2000; re
END.
Недостаток программы в том, что раздел операторов выглядит довольно мутно. Гораздо прозрачнее была бы такая запись:
BEGIN
re(3,1000); re(4,1000); re(3,2000)
END.
Для обеспечения такой прозрачности подходят процедуры с параметрами. Вот программа, использующая процедуру с параметрами:
USES CRT;
PROCEDURE re (octava : Byte; dlit: Word); BEGIN
if octava = 3 then Sound(587) else Sound(1174);
Delay(dlit);
NoSound END;
BEGIN
re(3,1000); re(4,1000); re(3,2000)
END.
Пояснения: Эта программа похожа на предыдущую, но имеется несколько отличий. Строка
Procedure re (octava : Byte; dlit: Word)
называется заголовком процедуры. Здесь после имени процедуры - re - ставятся скобки и внутри них описываются так называемые формальные параметры процедуры. Здесь их два: octava и dlit. Поскольку они описаны в заголовке, пропадает необходимость в разделе VAR.
В записи re(3,1000) числа 3 и 1000 - так называемые фактические параметры процедуры, их порядок и тип должен соответствовать формальным параметрам.
Когда во время выполнения программы Паскаль натыкается на re(4,1000), он присваивает переменной octava значение 4, переменной dlit - значение 1000 (то есть, присваивает формальным параметрам значения фактических параметров) и затем переходит к выполнению тела процедуры re.
Усложним задачу. Создадим универсальную процедуру nota, параметрами которой будут название ноты, октава и длительность. Учтем, что длительность ноты в музыке задается в так называемых долях. Доли бывают: 1 - целая нота, 1/2 - половинка длительности, 1/4 - четвертушка и т.д. Пусть целая нота звучит 1 секунду. Вызывать процедуру nota можно было бы так: nota(re,5,8) - это означает, что мы хотим, чтобы прозвучала нота re пятой октавы длительности 1/8.
Вот запись программы:
USES CRT;
TYPE Nota_type = (doo, doo_diez, re, re_diez, mi, fa, fa_diez, sol, sol_diez, la, la_diez, si);
PROCEDURE Nota(Nazvanie:Nota_type; Oktava,Dolya:Byte); {Здесь параметр Dolya - знаменатель доли}
VAR Hz:Word; {Внутри процедуры можно описывать свои переменные (в данном примере это Hz).
Они называются локальными. Подробнее о них - в 13.3}
BEGIN
{Объясним Паскалю частоту нужных нам нот третьей октавы}
case Nazvanie of
doo : Hz:=523;
re : Hz:=587;
sol : Hz:=784;
la : Hz:=880;
la_diez : Hz:=932;
end;
{Теперь меняем частоту в зависимости от октавы}
case Oktava of
1 : Hz:=Hz div 4; {Используем целочисленное деление,так как стандартная}
2 : Hz:=Hz div 2; {процедура Sound требует задавать частоту целым}
3 : Hz:=Hz; {числом герц}
4 : Hz:=Hz*2;
5 : Hz:=Hz*4;
6 : Hz:=Hz*8;
else WriteLn('Такой октавы не знаю'); ReadLn; Halt
end;
Sound (Hz); {Включаем звук}
Delay(10000 div Dolya); {Задаем пpодолжительность звука}
NoSound;
Delay (50); {Небольшой промежуток тишины после каждой ноты}
END;
BEGIN
{Вот первые ноты из песни “Широка страна моя родная”:}
Nota(re,3,8); Nota(re,3,16); Nota(re,4,4); Nota(re,4,8); Nota(re,4,8); Nota(doo,4,8);
Nota(la_diez,3,8); Nota(la,3,8); Nota(sol,3,4); Nota(re,3,4)
END.
Фактические параметры могут быть любыми выражениями подходящего типа. Например, вместо Nota(re,3,8) можно было бы написать a:=3; Nota(re, a, 11-a).
Задание 119: В модуле Graph не хватает процедуры, которая рисовала бы треугольник. Создайте такую процедуру. Она должна рисовать примерно равносторонний треугольник вершиной вверх и иметь три параметра: положение треугольника на экране и размер.
3.3.2 Функции
В 4.9 мы с вами уже сталкивались со стандартными функциями. Например, выражение 10+Sqr(3) имеет значение 19, так как функция Sqr(3) обозначает 32.
Вы можете создавать собственные функции. Предположим, вам часто приходится вычислять периметры прямоугольников. Тогда вам было бы удобно иметь функцию perimetr(10,4), которая имела бы значение периметра прямоугольника со сторонами 10 и 4. Рассмотрим, как это делается, на примере программы вычисления суммарной длины забора вокруг трех несоприкасающихся прямоугольных дворов:
FUNCTION perimetr(dlina,shirina:Word) : Integer;
BEGIN perimetr:=2*(dlina+shirina) END;
Подобные документы
Развертывание системы на жестком диске, диалоговая система программирования Турбо Паскаль, запуск программы и выполнение задания. Функциональные клавиши и их назначение. Текстовый редактор, средства создания и редактирования текстов программ, курсор.
реферат [18,6 K], добавлен 01.04.2010Основные сведения о системе программирования Турбо Паскаль. Структура программы на Паскале и ее компоненты. Особенности и элементы языка Турбо Паскаль. Порядок выполнения операций в арифметическом выражении, стандартные функции и оператор присваивания.
лекция [55,7 K], добавлен 21.05.2009Особенности использования графического режима в среде Турбо Паскаль. Типы драйверов. Инициализация графики. Построение изображения на экране. Графические примитивы и работа с текстом. Разработка и реализация программ в среде Турбо Паскаль "Графика".
курсовая работа [1,2 M], добавлен 26.09.2014Особенности программирования на языке Паскаль в среде Турбо Паскаль. Линейные алгоритмы, процедуры и функции. Структура данных: массивы, строки, записи. Модульное программирование, прямая и косвенная рекурсия. Бинарный поиск, организация списков.
отчет по практике [913,8 K], добавлен 21.07.2012Язык программирования Турбо Паскаль. Запись алгоритма на языке программирования и отладка программы. Правила записи арифметических выражений. Стандартное расширение имени файла, созданного системным редактором. Составной оператор и вложенные условия.
курсовая работа [75,0 K], добавлен 21.03.2013Использование графических возможностей Турбо Паскаля, подключение графического модуля Graph. Графические функции и процедуры. Общая структура графической программы. Построение фигур, определение цветов и стилей, работа с текстом, сообщения об ошибках.
реферат [109,3 K], добавлен 28.04.2010Правила описания множественных типов данных, приемов использования множеств и операций над множествами в Паскаль-программах. Разработка в Турбо Паскале программы вывода всех согласных букв, которые входят хотя бы в одно слово заданного предложения.
контрольная работа [30,8 K], добавлен 25.12.2010Освоение технологии структурного программирования и применения стандартных методов работы с одномерными массивами при разработке и создании программы на языке Турбо Паскаль. Разработка программы методом пошаговой детализации с помощью псевдокода.
реферат [276,9 K], добавлен 27.02.2008Разработка программы, создающей и управляющей базой данных, ее реализация на языке Turbo Pascal. Организация алгоритма программы. Вывод информации и возможность добавления информации в базу данных. Поиск информации в базе данных по заданному значению.
курсовая работа [26,7 K], добавлен 19.06.2010Расчет на устойчивость в системе программирования Турбо Паскаль. Определение критического напряжения стержня по формуле Ясинского, предельной гибкости в зависимости от материала. Программирование алгоритма расчета. Порядок ввода исходных данных.
курсовая работа [1,0 M], добавлен 27.04.2013