Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Самоучитель игры на Паскале. АВС и немного Турбо
.pdf
Лично моё эстетическое чувство как-то травмировала необходимость
передавать романтический цвет через какой-то математический integer.
Теперь у нас всё красиво – цвет передаётся как цвет. К сожалению, если
попытаться передать вместо TColor обычное целое, возражений не
последует. Как-то это неправильно. Сказано TColor, значит надо
объявлять как TColor, и чтобы никаких целых. И обратите внима
букву T в начале имени типа TColor. Есть хорошая традиция – все имена
типов
должны начинаться с буквы T. Так будет лучше, и Вам, и
окружающим.
А теперь объясню, к чему была вся эта теория. Захотели мы написать
процедуру для подсчёта суммы элементов массива. Интуиция
подсказывает, что сама процедура и её применение бу
примерно вот так:
const
N = 5;
var
a : array[1..N] of integer;
result : integer;
{-----------------------------------------------------------}
procedure Sum( a : array[1..N] of integer;
result : integer);
var
i : integer;
begin
result:=0;
for i:=1 to N do
result:=result + a[i];
end;
{-----------------------------------------------------------}
…………………………….
{заполнили чем-то массив}
Sum( a, result);
{вывели ответ}
дут выглядеть
ние на
141

Что произойдёт при попытке транслировать этот, с виду вполне
разумный, текст? Как говорится в народе, пролетела птица обломинго.
Ничего хорошего не произойдёт.
Ещё раз, в Pascal ABC ничего плохого не произойдёт, а вот в Delphi всё
очень даже и сразу накроется яшмовой чашей. Так что давайте с самого
начала делать так, что работало сраз
у и везде.
А как надо? А надо вот так:
const
N = 5;
type
TArray = array[1..N] of integer;
var
a : TArray;
result : integer;
{-----------------------------------------------------------}
procedure Sum( a : TArray;
result : integer);
var
i : integer;
begin
result:=0;
for i:=1 to N do
result:=result + a[i];
end;
{-----------------------------------------------------------}
…………………………….
{заполнили чем-то массив}
Sum( a, result);
{вывели ответ}
Просто запомните.
С многомерными массивами надлежит поступать точно так же. В смысле,
сначала объявить их типами.
142

Скучная, но необходимая теория
Какие бывают параметры и как их правильно готовить, мы в целом
разобрались. Теперь унылая тема под названием “Глобальные и
локальные переменные”. Когда-то тема эта была одной из важнейших при
изучении, пожалуй что, любого языка программирования. По моему
мнению, тему надо открыть и тут же закрыть. В приличной программе
ьных переменных быть не должно. В приличной программе вообще
глобал
много чего быть не должно. Подробнее на вопросе, чего не должен знать
хороший программист, мы
глобальных переменных не допустить в программу, надо знать, что это
такое. Врага надо знать в лицо!
Напишем вот такую совершенно абстрактную программу.
program gl
uses
Crt;
var
x,y : integer;
{--------------------------------------------------------------------}
procedu
var
x,z : integer;
begin
x:=10;
y:=20;
z:=30;
Writeln(‘x = ‘,x:2, ‘ y = ‘,y:2, ‘ z = ‘,z:2);
end;
{--------------------------------------------------------------------}
begin
x:=1;
y:=2;
Writeln(‘x = ‘,x:2, ‘ y = ‘,y:2);
MakeNothing;
Writeln(‘x = ‘,x:2, ‘ y = ‘,y:2);
end.
obal;
re MakeNothing;
остановимся как-нибудь позже. Но чтобы
143

Запустите программу на выполнение. Посмотрите на результат.
Подумайте. Сделайте выводы.
Для читающих эту книгу не подходя к компьютеру и не вставая с дивана,
привожу всё-таки результат её выполнения:
x = 1 y = 2
x = 10 y = 20 z = 30
x = 1 y = 20
Подумали? Сделали выводы?
Первая строка вывода никаких вопросов не вызывает. Что присвоили, то и
получили. И думать здесь, в об
А вторая строка интереснее. С одной стороны, опять-таки, что прис
то и вывели. Но переменные наши здесь не совсем равноправны. Y
объявлена в самой программе, “сверху”, так сказать. В процедуре она не
объявлена. X объявлена и там и там. Z объявлена только в процедуре.
А вот третья строка заставляет задуматьс
Значение X не изменилось, хотя в процедуре оно было другим. Значение
Y сохранилось. Z мы выводить и не пытались. Это и понятно, она
объявлена только в процедуре.
Теперь объясняем, сначала практически, потом теоретически.
Если переменная объявлена в программе, а в процедуре не объявлена, то
любые её изменения – в программе ли, в проц
навсегда.
Если переменная объявлена в
то изменения сделанные в процедуре, в ней и останутся. По выходе из
процедуры всё будет прощено и забыто.
Если переменная не объявлена в программе, а объявлена только в
процедуре – то всё стан
компилятора). Обратиться к этой переменной мы можем только в
процедуре. Снаружи её не видно.
Теперь обещанная теория. Заранее предупреждаю, что моё толкование
терминов может отличаться от приведённых в учебниках. В конце концов,
я столько программирую, что имею право на личное мнение в отношении
терминов. В моём коллективе я решаю, кто у ме
щем-то, не о чем.
я и, возможно, надолго.
едуре ли – действуют, и
программе, и в процедуре объявлена тоже,
овится гораздо проще (проще, в основн
ня локальная переменная!
воили,
ом, для
144

Немного выше я сказал о переменной, что её не видно. Это не случайно.
Наиважнейший термин – область видимости. Область видимости – откуда
видно переменную. Если переменная объявлена в процедуре, то её
область видимости ограничивается этой процедурой. Снаружи её не
видно. Если переменная объявлена в программе, её область видимости
поистине безгранична – переменную видно и в программе и в проц
Если переменная объявлена и в
деле, это совсем разные переменные. Ту переменную, что в процедуре,
видно только из процедуры, ту переменную, что объявлена в программе,
видно только в программе.
Переменные, объявленные в процедуре, называются локальными. Если
переменная объ
переменная для нас гл
они вполне могут существовать и это может даже вовсе от нас не зависеть
– если программу и процедуру пишут разные люди. Это зло неизбежное.
А вот зло, которого можно избежать – обращаться из процедуры к
глобальной переменно
А если процедур не одна, а две, и в каждой объявлены свои переменные?
Всё так же, но область видимости переменной, объявленной в одной
процедуре, этой процедурой и ограничивается, в другой процедуре эту
переменную видно не будет.
Ещё одно порождение больной фантазии – процедура, влож
другую процедуру. То есть, имеем программу, в программе процедура, в
процедуре другая процедура. Всё так же, как и было сказано выше, но
рол
ь программы выполняет первая процедура – а так всё сказанное о
глобальных и локальных переменных и об области видимости остаётся в
силе.
Вот так всё запутано.
явлена в программе, а мы сейчас в процедуре, то эта
обальная. Глобальные переменные – зло. То есть,
й. Не надо так делать.
программе, и в процедуре, то, на самом
перь функция
А те
едуре.
енная в
Вернёмся к нашему факториалу. Помните?
procedur
var result : integer);
var
e Fact( N : integer;
145

i : integer;
begin
result:=1;
for i:=1 to N do
result:=result * i;
end;
Соответственно, вызов этой процедуры и использование полученного
результата будет выглядеть так:
Fact( N, result);
Writeln( result);
А был бы у нас, к примеру, синус, могли бы так:
x:=Sin(y);
Writeln( x:8:2);
Или даже так:
Writeln( Sin(y):8:2);
Так, разумеется, проще и нагляднее. И более естественно и по
математически. Вот таким вот образом и оф
function Fact( N : integer) : integer;
var
result : integer;
i : integer;
begin
result:=1;
for i:=1 to N do
result:=result * i;
Fact:=result;
end;
А вот так это можно использовать:
ормим наш фактор
иальчик.
146

k:=Fact(N);
Writeln( k);
Writeln( Fact(N));
Это называется функция. А теперь разглядываем нашу функцию
внимательно. Принципиально важны две строчки. Первая – заголовок
функции. Вместо
procedure Fact( N : integer;
var result : integer);
пишем
function Fact( N : integer) : integer;
Выходной параметр result исчез из заголовка. От него остался только тип
integer, который переместился в конец заголовка функции. Зато result
появился в списке объявленных внутри функции переменных. Это не
ательно, но удобно в
обяз
процедура преобразуется в функцию.
И очень важная строчка
Fact:=result;
Слева – имя нашей функции. Справа, вообще говоря, может быть что
угодно. Если мы напишем Fact:=5, то значением нашей функции при всех
значениях аргумента будет неизменная пятёрка.
Этот оператор присваивания обеспеч
функцией.
Только не промахнитесь и не напишите наоборот, result:=Fact. Транслятор
проглотит, но результат выполнения будет нехороший. Это называется
рекурсия. Так тоже можно, но только если Вы очень точно знаете, чего
Вы хотите. А вообще, по-моему, так совсем не надо. Впрочем, к этому мы
ещё вернёмся.
Короче, без него ничего не получится.
случае, когда, как у нас, ранее написанная
ивает возврат значения нашей
147

Сочиним ещё какую-нибудь функцию. Для разнообразия, с результатом
логического типа. Функция должна проверять, является ли положительное
число точным квадратом. На вход – целое число, на выходе – логическая
переменная. Проверять будем в цикле, тупым перебором всех целых
чисел, меньших числа, заданного на вход. Для экономии будем
прекращать проверку, как только квадрат проверяемого чи
заданное число. Как я замечал ранее и ещё замечу позже, оптимизировать
программу по скорости – п
последнее, но не совсем никчёмное. В данном случае экономия может
быть весьма значительной. Цикл наш должен будет завершиться в
неизвестный заранее момент времени. Вообще-то, для таких случаев –
приращение по одн
известно заранее, но цикл может прекратиться раньше – очень хорошо
подходит обычный цикл for с использованием оператора Break. Но из
высших соображений мы используем цикл repeat-until. И обратите
внимание – нас совершенно не интересует, квадратом какого именно
числа является число, поступившее на вход функции. Нас только
интересует – да или нет? В результате вырисовывается такая вот функция:
function IsSqua
var
x : integer;
begin
x:=0;
repeat
x:=x+1;
until (x*x = N) or (x*x>N);
if x*x = N
then IsSquare:=true
else IsSquare:=false;
end;
А почему я не написал x*x>=N? Для наглядности. Чтобы чётко были
видны две причины нашего возможного выхода из цикла. Или нашли
точный квадрат, или перебрали все допустимые числа.
ому, начиная с единицы, максимальное знач
re( N : integer) : boolean;
оследнее дело. Обратите внимание –
сла превышает
ение
148

В качестве упражнения, оформите в виде функции написанный ранее
программный код, который считал количество слов в строке и выделял
слово с заданным номером из строки. Заголовки функций должны быть
примерно такими:
function WordCount( s : string) : integer;
function GetWord( s : string;
num : integer) : string;
А теперь тараканчик
Вот здесь я изобразил таракана. Как мог. Это было не
procedure Tarakan( x,y : integer;
color : integer);
begin
SetPenColor(color);
SetBrushColor( color);
{тельце}
Ellipse( x-35,y-20, x+35,y+20);
{головёнка}
Circle( x-45, y, 20);
{лапки}
Line( x,y-20, x,y-60);
Line( x-20,y-20, x-50,y-60);
Line( x+20,y-20, x+50,y-60);
Line( x,y+20, x,y+60);
Line( x-20,y+20, x-50,y+60);
Line( x+20,y+20, x+50,y+60);
{глазки}
SetBrushColor( clRed);
Circle( x-45, y-5, 3);
Circle( x-45, y+10,3);
end;
легко.
149

А теперь, чего я собственно хочу. А хочу я, чтобы тараканчик бегал. Ну,
или, для начала, перемещался по экрану. А как он может перемещаться?
Естественно, в цикле. Нарисовать, подождать, стереть, передвинуть и всё
снова. Настолько просто, что не надо даже сначала записывать в
комментариях (или, по научному, в псевдокоде). Пишем сразу:
x:=500;
y:=200;
for i:=1 to 300 do
begin
Tarakan( x,y, clGreen);
Dela
y(100);
Tarakan( x,y, clWhite);
x:=x-1;
end;
А теперь соберите из этого законченную программу. Не забудьте
объявить x,y,i. Ещё не забудьте про uses Crt, GraphABC. Запустите.
Должен забегать, хотя и помаргивая.
А как Вы можете поучаствовать в этом веселье?
Хочу, чтобы таракан перебирал лапками. Ну, или хотя бы, что бы каждая
лапка мо
параметра в процедуру рисования
гла быть в одной из трёх позиций. Напрашивается добавление
таракана – номер положения
конечности. Это может быть даже переменная цикла – только к ней надо
применить общеполезный оператор mod.
И ещё я хочу, чтобы, добежав до одной стенки (границы экрана), таракан
разворачивался и бежал в обратную сторону.
И, в конце концов, запустите тройку тараканов со сл
учайными
тараканьими скоростями, принимайте ставки и организуйте маленький
иппод
ром.
Надеюсь, эти просьбы не слишком сложны для Вас.
Да! И чтобы лапки в коленках сгибались!
А этот раздел просто больше некуда было вставить
Честно говоря, единственная причина, по которой этот раздел оказался
именно в этой главе – то, что результат предпочтительнее оформить в
виде процедур, точнее функций. Причина весьма формальная, но пусть
150
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
