Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Самоучитель игры на Паскале. АВС и немного Турбо

.pdf
Скачиваний:
0
Добавлен:
07.09.2026
Размер:
1 Мб
Скачать
Лично моё эстетическое чувство как-то травмировала необходимость передавать романтический цвет через какой-то математический 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
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]