Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
Как не делать ничего
У нас уже была программа, которая не делает ничего и строка, которая не
содержит ни одного символа. Двинемся дальше по этому заманчивому
пути – оператор, который не делает ничего. Как он называется? Пустой
оператор. Как он выглядит? Да никак. Точнее есть две школы. Согласно
первой, пустой оп
школе, пустой оператор фи
ератор состоит из точки с запятой. Согласно второй
гуры не имеет и места не занимает.
Иллюстрирую на примере. Это без пустого оператора:
Writeln(‘qwerty’);
А это с пустым оператором:
Writeln(‘qwerty’);;
Вот то, что находится между двумя точками с запятой и есть пустой
оператор. Что значит, там ничего нет? Ну да, ничего нет, так он ничего и
ает.
не дел
Вообще-то, Паскаль несколько чувствительно относится к лишним точкам с запятой. Точнее, не то, чтобы
чувствительно, а в строгом соответствии с синтаксисом языка. Точка с запятой между begin и end. Это, как и обещано,
пустой оператор. А вот лишняя точка с запятой в декларативной части программы, между program и begin, будет
раться и преследоваться.
жестока ка
Для чего пустой оператор нужен? Для чего хотите. А где его можно
использовать? Пустой оператор может использоваться во всех местах, где
может использоваться любой другой оператор. Сначала, плохие примеры.
Некоторые пишут вот так:
if x>0 then y:=100 else;
Это глупость, но безвредная. Убираем лишнее, остаётся
if x>0 then y:=100;
А некоторые пишут вот так:
if x>0 th
en else y:=200;
91

Поскольку перед else точка с запятой не ставится, пустой оператор
уменьшился до своего естественного вида, то есть до никакого.
Так вот, это тоже глупость, но очень вредная. Программиста надо долго
бить по рукам, затем повернуть ему мозги набок и написать так:
if x<=0 then y:=200;
Применим полученные навыки ничегонеделания к оператору цикла:
for i:=1 to 1000 do;
т тут уже не всё так просто. Пустой оператор не делает ничего.
А во
писанный нами оператор цикла не делает ничего тысячу раз. Но нельзя
На
сказать, что не делает совсем ничего – результата, конечно, нет, но
жужжать-то он при этом жужжит. Иными словами, отбирает ресурсы и
загруж
маленькое, но время, даже если внутри цикла абсолютно ниче
делается - накладные расходы. Пять старушек, как известно, рупь. А
цикл, ничего не делающий тысячу раз и тратящий на это “ничего” очень
маленькое время, потратит этого очень маленького времени в тысячу раз
больше и, вполне может быть, это будет уже не очень маленькое время.
Переведём безделье на качественно другой уровень –
вложенный цикл.
for i:=1 to 1000 do
for j:=1 to 1000 do;
Вложенных циклов у на пока не было. По плану, они будут позже.
Просьба над этим пока особо не задумываться. Если интуитивно понятно
– замечательно. Если не
Хоттабыч сваял телефон из цельного куска мрамора? Вот как к такому
цельносваянному телефону и относитесь.
Обратите внимание – ничего не делает тысячу раз только второй цикл.
Первый цикл при деле – он тысячу раз выполняет второй – поэтому после
первого do точка с запятой отсутствует.
ает процессор. На каждое выполнение цикла уходит хотя и очень
го не
ничего не делающий
понятно – неважно. Помните, как
Старик
92

А всего мы имеем – тысяча умножить на тысячу – миллион раз
отработавший впустую внутренний цикл. А это уже кое-что!
А теперь первый вопрос – почему транслятор выдаёт ошибку? Потому что
в нашей версии Паскаля пустые циклы недопустимы, а надо примерно вот
так:
for i:=1 to 1000 do
for j:=1 to 1000 do
int:=int+1;
И вечный вопрос – а зачем? Зачем вложенные циклы, которы
делают, только вр
Вернемся к примеру про пять параллельных линий.
SetPenColor(clGreen);
for i:=1 to 5 do begin
Line(100,200+10*(i-1), 300,200+10*(i-1));
end;
Программа выполняется последовательно, сначала рисуется первая линия,
затем вторая. Однако появляются они на экране одновременно. Нетрудно
понять, почему – появляются-то они поочерёдно, но нас
что это кажется одновременным. А если
по одной? Нельзя ли как-нибудь притормозить компьютер?
В теории всё очень просто. Есть процедура Delay. У неё один параметр,
указывающий задержку в миллисекундах – тысячных долях секунды. То
есть, если написать Delay(1), то программа на этом месте задержится на
одну миллисекунду, нич
будет ничего не делать ровно секунду. Пишем вот так:
SetPenColor(clGreen);
for i:=1 to 5 do begin
Line(100,200+10*(i-1), 300,200+10*(i-1));
Delay(1000);
end;
емя жрут?
мне хочется, чтобы медленно и
его не делая, а если Delay(1000), то программ
е ничего не
только быстро,
а
93

Не забываем при этом добавить строчки
uses
Crt;
На практике есть нюансы. На малых значениях параметра, Delay работает
неточно. Фиговенько он работает, прямо скажем, и требуемой задержки
Вы можете и не получить. Кроме того, с переходом от учебных задач к
реальным иногда возникает потребность установить задержку меньше
миллисекунды. И вот тут приходят на помощь на
ши, казалось бы
бессмысленные опыты. Сразу чешутся ручонки сделать что-нибудь
этакое:
for i:=1 to 5 do begin
Line(100,200+10*(i-1), 300,200+10*(i-1));
{здесь тормозим}
for i:=1 to 1000 do
for j:=1 to 1000 do
int:=int+1;
end;
Подберите числа под мощность Вашего компьютера.
Что-нибудь полезное
,
И не только полезное, но и красивое! Полезное, чтобы оно включало
циклы, массивы и вот это – про торможение программы. А красивое, чтоб
графика была. Хочу, чтобы было небо! И чтобы звёзды на нём! И,
главное, чтобы звёзды моргали!
Конкретизирую. Чёрное-чёрное небо – в смысле, чёрный-чёрный экран.
На нём сн
ачала нарисовано штук тридцать, думаю, этого хв
атит,
звёздочек. Звёздочки – это SetPixel. Затем какая-нибудь, совершенно
случайная звездочка гаснет, а в каком-нибудь другом, совершенно
случайном месте экрана звездочка загорается. Медленно и печально. И
так пока не надоест.
Какие практические выводы напрашиваются из этого технического
задания? Поскольку процесс явно длительный, должен быть цикл. Цикл
94

”пока не надоест” мы не умеем, пока. Напишем обычный цикл, до тысячи,
например – и пусть пользователь сам грубой физической силой всё это
останавливает. Поскольку звезды загораются и гаснут случайным
образом, обязательно возникнут Random и Randomize. Черное небо –
Rectangle, звёзды SetPixel. А раз должно быть медленно – должна быть
задержка. Это всё понятно. Дальше смутно ма
На слож
понятно.
program Stars;
uses
GraphABC;
var
i,j,k,n : integer;
begin
Randomize;
{чёрное-чёрное небо}
SetBrushColor(clBlack);
{нарисовать тридцать звездочек}
for i:=1 to 1000 do begin
{погасить случайную звезду}
{зажечь где-нибудь}
{задержка, подобрать по вкусу}
Delay(100);
end;
end.
Нарисовать тридцать звезд нетрудно. Цикл до тридцати, в
и все дела. В чём проблема? Проблема дальше – там, где звезду надо
погасить. Гасить мы её будем, естестве
SetPixel, только черным цветом. А вот по какому месту его применять?
Постепенно приходим к осознанию того факта, что местонахождение всех
ности не обращаем внимания и программируем то, что нам уже
SetPenColor(clBlack);
Rectangle(0,0, WindowWidth,WindowHeight);
ячат некоторые сложности.
нутри SetPixel –
нно, применяя по ней тот же
95

звёзд нам придется ещё и запомнить. Звёзд тридцать штук, значит, массив
будет из тридцати элементов. Другой вопрос – тридцати каких элементов?
К сожалению, на данном этапе нашего развития, могу только предложить
два массива по тридцать каждый – один для хранения координаты X,
другой для хранения координаты Y. Корявенько как-то? Корявенько, да.
А по-людски нельзя? По-людски, конечно, мо
новых сущностей и усвоения новых знаний. А голова, она не резиновая.
Так что, дел
Вспоминаем, что нам придётся случайным образом выбрать гасимую
звезду, и объявляем переменную для её номера и, заодно, и для её
координат.
Итого, в секции объявлений (и констант) у нас добавляются:
const
var
star
Кстати, имена переменным я дал не очень хорошие. Почему? Объясню
позже. Кому невтерпёж – читайте “Элементы стиля программирования”.
Теперь у нас есть, где запомнить координаты наших пикселов,
олицетворяющих собой наши звезды. Ри
for n:=1 to maxSta
x:=Random(WindowWidth);
y:=Random(WindowHeight);
SetPixel( x,y, clWhite);
starX[n]:=x;
starY[n]:=y;
А почему переменная цикла вдруг называется N? Потому что порядочные
девушки используют переменную цикла только для одной цели.
Переменная I у нас использована для главного цикла, J и K организуют
аем неэстетично, зато работоспособно.
maxStar = 30;
X : array[1..maxStar] of integer; {это координаты}
starY : array[1..maxStar] of integer;
star : integer; {номер гаснущей звезды}
x,y : integer; {координаты новой звезды}
суем и запоминаем:
r do begin
end;
жно, но требует введения
96

задержку, приходится задействовать N. Ничего, в запасе ещё M и L. Хотя,
конечно, если до них дойдёт дело, значит что-то не так. Переменным ведь
можно давать и осмысленные имена, не правда ли?
Что важнее, обратите внимание на Random(WindowWidth). Значение в
каком диапазоне он вернёт? Правильно, от нуля до WindowWidth-1.
Именно то, что нам и нужно, экранны
К сожалению, не всегда так удачно совпадает.
Теперь выбираем звезду случайным образом – одну из тридцати. Хочется
вот так – star:=Random(30). Согласно описанию функции Random получим
случайное число в диапазоне от 0 до 29. Надо бы на единицу больше.
Куда эту единицу добавить? Многим пишут так – star:=Random(30+1). Так
не надо. Надо вот так - star:=Random(30)+1. Запомните, не раз ещё
игодится. То
пр
не 30, а константа maxStar.
Теперь осталось
А) погасить звезду
Б) выбрать координаты новой звезды (случайным образом, разумеется)
В) зажечь звезду
Г) запомнить координаты
Переводим с русского на Паскаль. Русский текст оставляем в качестве
комментариев:
{погасить зве
SetPixel( starX[st
{выбрать координаты новой звезды}
x:=Random(WindowWidth);
y:=Random(WindowHeight);
{зажечь звезду}
SetPixel( x,y, clWhite);
{запомнить координаты}
{обратите внимание! Откуда стёрли, туда и рисуем!}
starX[star]:=x;
starY[star]:=y;
лько вспомните, что количество звёзд у нас на самом деле
зду}
ar], starY[star], clBlack);
е пиксели нумеруютс
я именно так.
97

Ну, вот вроде бы и всё. Но если можно без особого труда произвести
впечатление, почему бы и нет? Пусть звёзды будут разноцветные,
случайного цвета опять-таки. К сожалению, это не совсем просто. Будем
действовать по принципу – сначала прочитайте, потом дойдёт (до
головного мозга, в смысле, дойдёт).
Теория такова. Настоящий цв
представляет из себя, естеств
байт этого числа используется для загадочных, давно всеми забытых,
целей. Младший байт – красная составляющая цвета. Далее идут зелёная
и синяя компоненты. То есть, написав 0, получаем гарантированно
чёрный цвет – ведь там во всех байтах нули – потому что там вообще
везде нули, и кро
пол
учим тёмно красный, а если 255, то получим самый, что ни на есть,
яркий красный цвет. С двумя остальными цветами чуть сложнее и мы
тут пока об этом не будем, так что поверьте дальнейшему внезапному
выводу.
Объявляем переменную и пишем:
color:=Random(255*255*255) ;
Ну а теперь соби
program Stars;
uses
Crt,
GraphABC;
const
maxStar = 30;
var
starX : array[1..maxStar] of integer; {это координаты}
starY : array[1..maxStar] of integer;
star : integer; {номер гаснущей звезды}
x,y : integer; {координаты новой звезды}
color : integer; {цвет звезды}
i,j,k,n : integer;
begin
{чёрное-чёрное небо}
ме нулей ничего нет! Если задать цвет равным 50, то
раем всё вместе, стараясь ничего не потерять по дороге.
ет (TrueColor) занимает четыре байта и
енным образом, целое число. Старший
98

SetBrushColor(clBlack);
SetPenColor(clBlack);
Rectangle(0,0, WindowWidth,WindowHeight);
{нарисовать тридцать звездочек}
for n:=1 to maxStar do begin
x:=Random(WindowWidth);
y:=Random(WindowHeight);
SetPixel( x,y, clWhite);
starX[n]:=x;
starY[n]:=y;
end;
for i:=1 to 1000 do begin
star:=Random(maxStar) + 1;
{погасить звезду}
SetPixel( starX[star], starY[star], clBlack);
{выбрать координаты новой звезды}
x:=Random(WindowWidth);
y:=Random(WindowHeight);
{зажечь звезду}
color:=Random(255*255*255);
SetPixel( x,y, color);
{запомнить координаты}
{обратите внимание! Откуда стёрли, туда и рисуем!}
starX[star]:=x;
starY[star]:=y;
{задержка, подобрать по вкусу}
Delay(100);
end;
end.
Работает? Работает! Но звёздочек маловато. До
Совсем забыли, как же клиент всё это великолепие прекратит, когда
надоест? Смотри
не самый тяжелый случай – цикл выполняется долго, но не вечно. А
те и запоминайте, пригодится. Не только здесь. Тут у нас
бавьте до ста.
99

бывает, что и вечно – не нарочно, разумеется. Ещё не умеете писать
вечных циклов? Научим!
Когда программа входит в бесконечный цикл, про неё говорят, что она
зациклилась. Это очень, очень плохо.
Как с этим бороться? Очень просто. Нажимаем
Ctrl/F2 (если вам нравится
клавиатура) или тычем мышкой в страшную красную кнопочку с
надписью STOP. И всё. Вернулись. Внизу, под нашей программой
появляется меланхоличное сообщение «Программа прервана
пользователем».
Метод очень хороший, но срабатывает, только когда наша программа
запущена из-под среды Паскаля. Если запущен готовый исполняемый
файл, это не поможет, придётся убивать программу дру
гими способами.
Что делать? Вообще говоря, программировать аккуратнее, не допуская
зацикливания. Ко
проверять, не нажата ли клавиша
нкретно в нашем случае, при каждом выполнении цикла
Esc или что-то вроде того, и, в случае
нажатия, прекращать исполнение. Как это сделать, скоро узнаем.
TP. В чём разница
Глава эта была очень длинная и рассказывала о самых разных, хотя и
тесно связанных между собою, предметах. А разницы почти никакой.
Разницы с TP, в смысле. Всё, что касается циклов и ма
ссивов, применимо
безо всяких изменения.
По причинам технического характера процедура Delay толком не работает
в TP (хотя и присутствует). Так что п
ридётся применять его замену с
помощью пустых циклов, здесь же чуть ранее описанную. TrueColor
цвета в TP отсутствуют, так что вместо строки
color:=Random(255*255*255);
можно будет написать
color:=Random(15) + 1;
Разницы даже и не почувствуете.
Ctrl/F2 прекрасно работает и в TP.
100
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
