ВВЕДЕНИЕ
Система программирования Турбо Паскаль представляет собой единство двух в известной степени самостоятельных начал: компилятора с языка программирования Паскаль (язык назван в честь выдающегося французского математика и философа Блеза Паскаля (1623-1662)) и некоторой инструментальной программной оболочки, способствующей повышению эффективности создания программ.
Паскаль – замечательный язык программирования, который относительно прост в изучении, довольно ясен и логичен и, будучи первым изучаемым языком программирования, приучает к хорошему стилю. Паскаль воспитывает дисциплину структурного программирования и программирования вообще лучше, чем другие языки программирования, такие, как, например, БЕЙСИК.
Паскаль – гибкий и развитый в отношении типов данных язык. Привлекательны его рекурсивные возможности, а также поддержка технологии объектно-ориентированного программирования.
Изучение программирования на языке Паскаль может дать хороший старт в огромный и увлекательный мир программирования. Обучение языку программирования проходит намного более эффективно с изучением примеров.
Чаще всего (процедурное) программирование использует итерации, то есть циклы; однако рекурсия – описание объекта или вычисления в терминах самого себя – является более простым математическим понятием, а также мощной, но мало используемой техникой программирования.
Некоторые программисты считают (и не без оснований), что рекурсия – это сердце и душа языка Паскаль. В этой работе мы рассмотрим применение рекурсии в программах на Паскале. Здесь рассматриваются примеры рекурсивных алгоритмов и программирование комбинаторных вычислений.
Ко всему прочему мы научимся представлять данные в памяти ЭВМ и разрабатывать программы в среде Турбо Паскаль.
Постановка задачи
Написать программу-игру.
Игра-Спички. Дано 100 спичек. В игре принимают участие 2 игрока. Каждый, из которых может взять от 1 до 10 спичек за один ход. Тот, чья очередь подойдет, когда в кучке останется 1 спичка – проиграл.
Описание методов
Использование модуля CRT для очистки экрана и модуля GRAPH для графического отображения. Основными процедурами, использовавшимися в программе, являются графические процедуры типа LINE(X1,Y1,X2,Y2:integer), OutTextXY(X,Y:integer; TextString:string), SetFillStyle(Pattern:word; Color:word), Bar(X1,Y1,X2,Y2:integer). Применение условных операторов IF... THEN...ELSE..., использование циклов WHILE ... DO ... , REPEAT ... UNTIL .... change(var A,B:string) – процедура, написанная внутри программы.
LINE(X1,Y1,X2,Y2:integer) – процедура, которая рисует линию от точки с координатами X1,Y1 до точки с координатами X2,Y2
OutTextXY(X,Y:integer; TextString:string ) - выводит заданный текст TextString, начиная с координаты первой буквы, которые задаются по осям X,Y
SetFillStyle(Pattern:word; Color:word) – процедура, указывающая цвет заполнения. Используется вместе с процедурой Bar(X1,Y1,X2,Y2:integer)
Bar(X1,Y1,X2,Y2:integer) – процедура, которая рисует прямоугольник, закрашиваемый с помощью процедуры SetFillStyle(Pattern:word; Color:word)
Оператор IF...THEN...ELSE... используется для выражения условия выполнения определенного действия, после которого выполняется другое определенное действие при условии, что было выполнено предыдущее действие, иначе выполняется другое действие
FOR I:=A TO N DO... оператор цикла, который используется для повторения определенного действия или нескольких определенных действии, начиная с A до N-го раза.
Change(var A,B:string) – процедура, позволяющая менять местами две переменные A и B.
Разработка алгоритма
Прежде всего, у нас есть 2 игрока. И не зависит от того, кто ходит, и нам не нужно знать кто, сколько спичек взял всего. Что упрощает нашу задачу. Но тем не менее, есть 100 спичек. Каждый раз нам необходимо отнимать то количество спичек, которое возьмет любой из игроков, но необходимо учитывать, что разрешено брать только от 1 до 10 спичек. Поэтому здесь необходимо ограничить игроков. Пусть а – количество спичек на данный момент, x – количество взятых спичек на данный момент. Тогда организуем цикл WHILE ... DO ...:
while a>1 do
begin
…
end;
В начале имеем 100 спичек, то есть а=100. Заведем переменную l типа boolean. Пусть ее значение будет false. Тогда, далее напишем еще один цикл:
if a in [2..100] then
repeat
…
until l=true;
Переменная l примет значение true в том случае, когда взято от 1 до 10 спичек. Считаем переменную x:
readln(x);
Если ввели от 1 до 10, то:
if (x<1>10) then
begin
…
end
Иначе, если осталось меньшее количество спичек по отношению к тому, сколько запрашивают взять:
else
begin
if a-x<1>
begin
…
end
Иначе, в интересующем нас варианте:
else
begin
…
a:=a-x;
l:=true
end;
Здесь помимо присваивания l значения true, есть еще одно действие, которое показывает, сколько же спичек на данный момент осталось.
На этом заканчивается расчет программы. Но еще остается оформление с использованием графики, то есть модуля GRAPH.
Для начала выведем правила игры. Расположим их в верхней части экрана:
outTextXY(300,10,'IGRA: 100 spi4ek');
outTextXY(10,20,'Pravila igri: pervona4alno imeetsa 100 spi4ek');
outTextXY(10,30,'Za odin hod igrok moget brat ot odnoy do 10 spi4ek');
outTextXY(10,40,'Tot u kogo ostanetsa poslednyaya spi4ka, tot viigral');
Далее нам необходимо нарисовать все 100 спичек. Сделаем это следующим образом: выведем 4 ряда по 25 спичек в каждом. Расположим их ниже условия – по центру экрана:
xx:=200;
y:=200;
for i:=1 to 25 do
begin
line(xx,y,xx,y+10);
line(xx,y+15,xx,y+25);
line(xx,y+30,xx,y+40);
line(xx,y+45,xx,y+55);
xx:=xx+5;
end;
Здесь xx – начальная координата по X, а y – начальная координата по Y.
Теперь необходимо очистить верхнюю часть экрана, где мы выводили условие. Для этого воспользуемся двумя процедурами:
setfillstyle(black,black);
bar(0,0,600,200);
С помощью которых программа рисует прямоугольник с координатами верхнего левого угла 0,0 и координатами нижнего правого угла 600,200.
И вся фигура закрашивается черным цветом.
В дальнейшем мы будем использовать именно этот способ затирания ненужных элементов текста.
Теперь предстоит одно из самого сложного в этой программе. Необходимо каждый раз как только будут взяты спички, удалять это количество из общей кучи. Для этого разобьем весь процесс на 4 части,
когда:
количество спичек меньше 25,
количество спичек больше 25, но меньше 50,
количество спичек больше 50, но меньше 75,
количество спичек больше 75
Пусть переменная kol – количество спичек, оставшихся в куче, на данный момент, тогда:
if kol<26>
begin
kol:=kol+x;
setfillstyle(black,black);
bar(xx,y,xx+(x-1)*5+5,y+10);
xx:=xx+(x-1)*5+5;
if kol>25 then
begin
x:=kol-25;
kol:=26;
xx:=195;
end;
end;
if (kol>25) and (kol<51>
begin
…
y:=215;
if kol>50 then
begin
x:=kol-51;
kol:=51;
xx:=195;
end;
end;
if (kol>50) and (kol<76>
begin
…
y:=230;
if kol>75 then
begin
x:=kol-76;
xx:=195;
end;
end;
if (kol>75) and (kol<101>
begin
y:=245;
…
end;
Здесь удаление спичек происходит с помощью тех же процедур, что и затирание условия задачи:
setfillstyle(black,black);
bar(xx,y,xx+(x-1)*5+5,y+10);
xx:=xx+(x-1)*5+5;
Здесь главное заключается в постановке координат. Каждый раз должно удаляться некое количество спичек, начиная с последней взятой, что обеспечивается присваиванием переменной xx нового значения xx+(x-1)*5+5, где x – количество взятых спичек.
Следует учесть, что координаты каждой сточки изменяются так, что y увеличивается на 15, а x должен вновь стать равным 195, что обеспечивается с помощью условного оператора:
y:=215;
if kol>50 then
begin
x:=kol-51;
kol:=51;
xx:=195;
end;
После того, как игра заканчивается, экран заново очищается, но на этот раз полностью:
setfillstyle(black,black);
bar(0,0,800,400);
А далее выводится информация о результате игры, а также веселая улыбка. Текст выводится с помощью OutTextXY(), а рисунок:
setcolor(red);
setLineStyle(0,0,3);
circle(100,300,60);
circle(70,290,10);
circle(130,290,10);
arc(100,310,200,340,30);
setcolor(14);
line(20,220,50,250);
line(50,190,80,230);
line(120,230,150,190);
line(150,250,180,220);
arc(X,Y:integer; StAngle,EndAngle,Radius:word) – процедура, рисующая полукруг с координатами центра X,Y, радиусом Radius, StAngle, EndAngle – угол поворота.
Листинг:
program kursovaya3;
uses crt,graph;
var a,x,y,i,xx,kol:integer;
ga,gm,error:integer;
b,c,s:string;
l:boolean;
procedure change(var j,h:string);
var q:string;
begin
q:=j;
j:=h;
h:=q;
end;
begin
clrscr;
ga:=detect;
gm:=detect;
initgraph(ga,gm,'c:\Pascal\UNITS');
error:=graphresult;
if error<>grok then
begin
writeln('error! Video Driver doesnt found',#10#13,grapherrormsg(error));
halt;
end;
a:=100;
outTextXY(300,10,'IGRA: 100 spi4ek');
outTextXY(10,20,'Pravila igri: pervona4alno imeetsa 100 spi4ek');
outTextXY(10,30,'Za odin hod igrok moget brat ot odnoy do 10 spi4ek');
outTextXY(10,40,'Tot u kogo ostanetsa poslednyaya spi4ka, tot viigral');
xx:=200;
y:=200;
for i:=1 to 25 do
begin
line(xx,y,xx,y+10);
line(xx,y+15,xx,y+25);
line(xx,y+30,xx,y+40);
line(xx,y+45,xx,y+55);
xx:=xx+5;
end;
outTextXY(10,60,'Vvedite imya pervogo igroka');
readln(b);
outTextXY(10,70,'Vvedite imya vtorogo igroka');
readln(c);
xx:=195;
while a>1 do
begin
l:=false;
if a in [2..100] then
repeat
begin
{delete}
setfillstyle(black,black);
bar(0,0,600,200);
{end of delete}
outTextXY(20,20,'v ku4ke imeetsa: ');
str(a,s);
outTextXY(160,20,s);
outTextXY(210,20,' spi4ek');
outTextXY(20,30,b);
outTextXY(40+length(b),30,' vash hod:');
readln(x);
if (x<1>10) then
begin
outTextXY(20,40,'Vi popitalis vzat ');
str(x,s);
outTextXY(160,40,s);
outTextXY(190,40,' spi4ek, a mogno tolko v predelah ot 1 do 10')
end
else
begin
if a-x<1>
begin
outTextXY(20,50,'Ostalos ');
str(a,s);
outTextXY(90,50,s);
outTextXY(130,50,'spi4ek, poetomu max, shto vi mogete vzat = ');
str(a-1,s);
outTextXY(20,60,s)
end
else
begin
outTextXY(20,70,b);
outTextXY(30+length(b),70,', vash hod prinat');
outTextXY(20,80,'Nagmite "Enter dlya prodolgenia"');
a:=a-x;
l:=true;
{delete}
setfillstyle(black,black);
bar(20,20,300,30);
if kol<26>
begin
kol:=kol+x;
setfillstyle(black,black);
bar(xx,y,xx+(x-1)*5+5,y+10);
xx:=xx+(x-1)*5+5;
if kol>25 then
begin
x:=kol-25;
kol:=26;
xx:=195;
end;
end;
str(kol,s);
outTextXY(20,90,s);
if (kol>25) and (kol<51>
begin
kol:=kol+x;
y:=215;
setfillstyle(black,black);
bar(xx,y,xx+(x-1)*5+5,y+10);
xx:=xx+(x-1)*5+5;
if kol>50 then
begin
x:=kol-51;
kol:=51;
xx:=195;
end;
end;
if (kol>50) and (kol<76>
begin
kol:=kol+x;
y:=230;
setfillstyle(black,black);
bar(xx,y,xx+(x-1)*5+5,y+10);
xx:=xx+(x-1)*5+5;
if kol>75 then
begin
x:=kol-76;
xx:=195;
end;
end;
str(kol,s);
outtextXY(20,90,s);
if (kol>75) and (kol<101>
begin
y:=245;
setfillstyle(black,black);
bar(xx,y,xx+(x-1)*5+5,y+10);
xx:=xx+(x-1)*5+5;
end;
{end of delete}
end;
end;
readln;
end;
until l=true;
change(b,c);
setcolor(black);
outTextXY(30+length(b),70,', vash hod prinat');
outTextXY(20,80,'Nagmite "Enter dlya prodolgenia"');
setcolor(white);
end;
setfillstyle(black,black);
bar(0,0,800,400);
outTextXY(20,100,'Pozdravlau s viigrishem igrok ');
outTextXY(260,100,b);
outTextXY(250,10,'IGRA: 100 spi4ek');
outTextXY(250,200,'THX FOR USING IT');
outTextXY(300,400,'KURSOVAYA: IGRA-SPI4KI, ALMATY, 2005');
setcolor(red);
setLineStyle(0,0,3);
circle(100,300,60);
circle(70,290,10);
circle(130,290,10);
arc(100,310,200,340,30);
setcolor(14);
line(20,220,50,250);
line(50,190,80,230);
line(120,230,150,190);
line(150,250,180,220);
readln;
closegraph
end.
Заключение
Итак, подведём итоги. Мы научились разрабатывать программы в среде Турбо Паскаль, строить к ним блок-схемы и представлять данные в памяти ЭВМ. Также мы познакомились с “маленьким чудом” в программировании – рекурсией и рекурсивными алгоритмами.
Так почему же используют рекурсию? Дело в том, что многие алгоритмы можно изящно и надёжно написать с помощью рекурсии, в то время как итерационное решение трудно запрограммировать и легко сделать ошибки. Примером тому служат алгоритм быстрой сортировки и алгоритмы обработки древовидных структур данных. Языковые понятия опираются исключительно на рекурсию, а не на итерацию. Даже для обычных языков типа С и Паскаль рекурсию, вероятно, следует использовать более часто, чем это делается, из-за краткости и ясности программ, которые получаются в результате.
Рекурсия отражает черту абстрактного мышления, проявляющуюся в самых различных приложениях (в математике, синтаксическом анализе и трансляции, древовидной сортировке и обработке данных с динамической структурой, шахматных задачах и т.д.). Пользование ею избавляет от необходимости последовательного (и часто, утомительного) описания процессов.
Таким образом, рекурсия не является чем-то нарочито усложнённым и предназначенным для касты посвящённых, а представляет собой ещё одно средство программирования, которым можно пользоваться удачно или злоупотреблять, как и всяким другим.
Урок таков: следует избегать рекурсивного решения там, где есть очевидное итеративное решение, и использовать его тогда, когда без рекурсии просто не обойтись. “Итерация свойственна человеку, а рекурсия – богу” (Л. Питер Дойч).
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ:
Бен-Ари М. Языки программирования. Практический сравнительный анализ: Пер. с англ. – М.: Мир, 2000. – 366 с., ил.
Зуев Е.А. Turbo Pascal. Практическое программирование. – М.: Приор, 1997. – 336с.
Кнут Д. Искусство программирования, том 1. Основные алгоритмы, 3-е изд.: Пер. с англ. – М.: Издательский дом "Вильямс", 2000. – 720 с.
Немнюгин С.А. Turbo Pascal. – СПб.: Издательство “Питер”, 2000. – 496 с., ил.
Немнюгин С.А. Turbo Pascal: практикум – СПб.: Питер, 2001. – 256 с., ил.
Рубенкинг Н.Дж. Турбо Паскаль для Windows: в 2-х томах. Т.1. Пер. с англ. - М.: Мир, 1993. – 536 с., ил.
Рубенкинг Н.Дж. Турбо Паскаль для Windows: в 2-х томах. Т.2. Пер. с англ. - М.: Мир, 1993. – 536 с., ил.