Учебник по программированию.

Первые шаги. Язык программирования PascalABC.

Предыдущий параграф Назад в содержание Следующий параграф


§15. Сведения по подпрограммам.

В четвёртом параграфе мы познакомились с процедурами и функциями. Однако на тот момент мы не могли изучить все нюансы. Теперь, когда вы получили опыт программирования, этот вопрос можно изучить более подробно.


Работа подпрограммы с переменной напрямую.

При изучении процедур и функций для работы со строками было обещано, что в этом параграфе мы подробно изучим использование слово var при объявлении процедур и функций.

Как было сказано, слово var используется в том случае, если мы хотим что бы процедура или функция работала с переменной напрямую. При обычном объявлении, без использования слова var, создаётся как бы копия передаваемого параметра, и работа подпрограммы ведётся с этой копией. При использовании слова Var подпрограмма, изменяя свою переменную, при этом меняет ту переменную, которая была передана в качестве параметра. Например, в следующем примере процедура summ в своём теле, изменяя значение переменной y, меняет значение переменной i, которая находится в теле основной программы:


var i:integer;

Procedure summ(x:integer;var y:integer);

begin

  y:=x+y;

end;

begin

  i:=2;

  writeln('i=',i);

  summ(5,i);

  writeln('После вызова процедуры summ i=',i);

end.

_______________________________________________

i=2

После вызова процедуры summ i=7


Во время работы процедуры summ будет создана переменная x, которая примет значение 5, а переменная y будет указывать на переменную i и не будет занимать место в памяти компьютера.

Без использования слова var для решения такой задачи нам пришлось бы создать функцию, и программа выглядела бы следующим образом:


var i:integer;

function summ(x,y:integer):integer;

begin

  summ:=x+y;

end;


begin

  i:=2;

  writeln('i=',i);

  i:=summ(5,i);

  writeln('После вызова функции summ i=',i);

end.

____________________________________________

i=2

После вызова функции summ i=7


Как видно результат тот же самый, да и строчек такое же количество, однако во время работы данной функции в памяти будет создано две переменные x и y. Переменная x примет значение 5, а переменная y «возьмёт» значение у переменной i. В итоге в памяти компьютера будет отведено место под две переменные.

А теперь давайте представим, что нам надо передать в подпрограмму в качестве параметра большой массив или огромную строку, в таком случае, в целях экономии ресурсов, лучше воспользоваться словом var.

Так же удобно пользоваться данным словом, если нам необходимо в подпрограмме изменить две и более переменных из основной программы. Следующий пример демонстрирует такой случай:


var m:array [1..2] of integer;

procedure perestanovka(var x,y:integer);

var temp:integer;

begin

  temp:=x;

  x:=y;

  y:=temp;

end;


begin

  m[1]:=2;  m[2]:=5;

  writeln('m[1]=',m[1],'  ','m[2]=',m[2]);

  if m[1]<m[2] then perestanovka(m[1],m[2]);

  writeln('После сортировки');

  writeln('m[1]=',m[1],'  ','m[2]=',m[2]);

end.

_____________________________________________

m[1]=2  m[2]=5

После сортировки

m[1]=5  m[2]=2


Конечно, можно было перестановку значений в массиве организовать внутри цикла for to do, однако если вам необходимо в разных местах программы произвести перестановку, то создав данную процедуру, вам не придётся писать один и тот же код несколько раз. В данном случае уместно вспомнить программистскую поговорку: «Не пиши код дважды».

И ещё одно. Будьте внимательны, если в данном примере вы не напишите слово var, то ошибки при компиляции не выйдет, так же программа будет работать без ошибок, однако перестановки значений не произойдёт. Можете проверить сами.


Вызов подпрограммы подпрограммой.

До сих пор мы вызывали подпрограммы из тела основной программы. Так же мы умеем создавать подпрограмму внутри подпрограммы. Есть ещё возможность вызывать внутри одной подпрограммы другую подпрограмму. Т.е. любая подпрограмма может вызывать другую подпрограмму. Для этого достаточно просто обратиться к ней по имени. Далее пример программы, в которой происходит сортировка массива с помощью отдельной подпрограммы. Как раз эта подпрограмма во время своей работы и вызывает другие две:


Program sortirovka_massiva;

Var m:array [1..10]of integer;


Procedure Vivod;

begin

  for var i:=1 to 10 do

    begin

     write(m[i]:4,'  ');

     if (i mod 5) = 0 then writeln;

    end;

  writeln;

end;


Procedure perestanovka(var x,y:integer);

begin

  var temp:=x;

  x:=y;

  y:=temp;

end;


Procedure sortirovka;

var flg:=true;

    shag:integer;

begin

  shag:=0;

  while flg do

    begin

      flg:=false;

      for var i:=1 to 9 do

        if m[i]>m[i+1] then

          begin

            perestanovka(m[i],m[i+1]);

            flg:=true;

            inc(shag);

            Writeln(shag,'-й шаг сортировки');

            vivod;

          end

    end;

end;


begin

  for var i:=1 to 10 do m[i]:= random(0,100);

  writeln('Имеем следующий список случайных чисел:');

  vivod;

  writeln('Начинаем сортировку');

  sortirovka;

  Writeln('После сортировки список стал следующим:');

  vivod;

end.

____________________________________________________

Имеем следующий список случайных чисел:

  52    70    76    30    81 

  85    11    35    89    13 


Начинаем сортировку

1-й шаг сортировки

  52    70    30    76    81 

  85    11    35    89    13 


2-й шаг сортировки

  52    70    30    76    81 

  11    85    35    89    13 

______________________________________________________

......................................................

______________________________________________________


21-й шаг сортировки

  11    30    13    35    52 

  70    76    81    85    89 


22-й шаг сортировки

  11    13    30    35    52 

  70    76    81    85    89 


После сортировки список стал следующим:

  11    13    30    35    52 

  70    76    81    85    89 


Примечание: В целях экономии бумаги из результата работы программы были убраны с 3-его по 20-й шаги.

Данная программа демонстрирует, как происходит сортировка массива по шагам. На каждом шагу происходит перемещение меньшего элемента влево. Для вывода массива на экран используется процедура vivod. Она вызывается как в теле самой программы, так и в теле процедуры sortirovka. Так же в теле процедуры sortirovka вызывается процедура perestanovka.

В принципе тут всё ясно, ничего сложного нет. Однако есть один нюанс. Из одной подпрограммы вызвать другую можно только в том случае, если вызываемая подпрограмма описана выше по тексту. Т.е. если рассматривать данный пример, то мы не можем внутри процедуры vivod вызвать процедуру sortirovka.

Конечно, в данном примере в этом нет необходимости, однако бывают случаи, когда две разные подпрограммы должны вызывать друг друга. В таком случае используется слово forward. Делается это следующим образом:


procedure P_2; forward;

procedure P_1;

begin

  ...

  P_2;

  ...

end;

procedure P_2;

begin

  ...

  P_1;

  ...

end;


Слово forward означает, что данная подпрограмма существует и будет описана ниже. Это слово нельзя использовать для уже описанных подпрограмм.


Рекурсия.

Любая подпрограмма может вызывать не только другую подпрограмму, но и саму себя. Это называется рекурсией.

При рекурсии подпрограмма как бы вызывает саму себя, однако при этом происходит создание в памяти компьютера новой такой же подпрограммы со всеми её переменными. Причём во время работы этой новой подпрограммы  она снова вызывает саму себя, и снова происходит создание в памяти компьютера новой такой же подпрограммы. И так далее. Поэтому при использовании рекурсии необходимо продумывать условия выхода.

С точки зрения экономного использования ресурсов компьютера, рекурсивный метод может оказаться расточительным, однако в каких-то случаях рекурсией можно пользоваться. Тем более при использовании рекурсии код получается короче и нагляднее.

В одном из предыдущих параграфах в одной из задач необходимо было с помощью цикла найти факториал числа. С помощью рекурсии программа нахождения факториала будет выглядеть следующим образом:


Program Factorial_Recursiya;

function Fac(n:integer):longint;

begin

if n=1 then

   fac:=1

  else

   fac:=n*fac(n-1);

end;


begin

  writeln('Факториал числа 9 равен ',fac(9));

end.

______________________________________________

Факториал числа 9 равен 362880


Наглядно работу рекурсии можно изобразить следующим образом:

Думаю, что после такого наглядного изображения с рекурсией всё понятно.


Специальная переменная Result.

В отличии от TurboPascal в PascalABC.NET существует специальная переменная, которую можно использовать вместо имени функции для присвоения результата работы функции. Т.е. что бы присвоить возвращаемое функцией значение можно присвоить это значение специальной переменной result. Пример:


function f:integer;

begin

  result:=3;

end;

begin

  writeln(f);

end.

___________________

3



Подпрограммы с одним именем.

Следующий в данном подразделе материал относится только к PascalABC.NET.

В одном из предыдущих примеров была приведена программа sortirovka_massiva, в которой была использована процедура с именем perestanovka, которая меняла местами значения двух переменных. Допустим, нам необходимо написать программу сортировки не только массива целых чисел, но и вещественных. Тогда нам придётся написать две процедуры перестановки значений. Одна будет менять местами значения целых чисел, другая вещественных. При этом названия процедур можно сделать одинаковыми. Делается это следующим образом:


Procedure perestanovka(var x,y:integer);

begin

  var temp:=x;

  x:=y;

  y:=temp;

end;


Procedure perestanovka(var x,y:Real);

begin

  var temp:=x;

  x:=y;

  y:=temp;

end;


begin

var i:=3; var k:=4;

writeln('До  вызова  процедуры  perestanovka i=',i,' k=',k);

perestanovka(i,k);

writeln('После вызова процедуры perestanovka i=',i,' k=',k);

var r:=5.7; var s:=6.3;

writeln('До  вызова  процедуры  perestanovka r=',r,' s=',s);

perestanovka(r,s);

writeln('После вызова процедуры perestanovka r=',r,' s=',s);

end.

______________________________________________________________

До  вызова  процедуры  perestanovka i=3 k=4

После вызова процедуры perestanovka i=4 k=3

До  вызова  процедуры  perestanovka r=5.7 s=6.3

После вызова процедуры perestanovka r=6.3 s=5.7


У обеих процедур одинаковое имя, но разные типы параметров. При вызове процедуры из тела программы, нужная процедура выбирается автоматически, исходя из типа передаваемых параметров. Разными могут быть не только типы передаваемых параметров, но и их количество. В таком случае нужная функция будет выбираться исходя из типа параметров и их количества. Если нужная функция с определённым числом и типом параметров не будет найдена, то при компиляции выйдет ошибка.

Когда в программе существуют подпрограммы с одинаковыми именами, они называются перегруженными. Написание подпрограмм с одинаковыми именами называется перегрузкой.

Явным примером перегруженной подпрограммы является, до боли знакомая нам, процедура write. В качестве параметра можно передать переменную любого типа, а так же параметров может быть сколь угодно.

В принципе всё просто, однако есть нюансы.

В следующем примере обе процедуры считаются одинаковыми, и при компиляции выйдет ошибка:


procedure p(i,k:integer);


procedure p(var i,k:integer);


Т.е. нельзя писать две подпрограммы с одним именем, с одинаковыми передаваемыми параметрами, но одна со словом var, другая без него.

Ещё один нюанс. Если в функции передаваемые параметры имеют тип real, то в неё можно передать и переменные целого типа. Т.е. нет необходимости писать две перегруженные функции, достаточно одной с передаваемыми параметрами типа real.

И ещё один нюанс. Если имеем следующие перегруженные процедуры:


procedure p_1(i,k:integer);

procedure p_1(i:integer,r:real);


И если процедура будет вызвана следующим образом: p_1(1,2), то к данному вызову одинаково подходят обе функции, по этому так же возникнет ошибка при компиляции.


В данном параграфе мы научились работать с переменными в подпрограммах напрямую, вызывать подпрограмму из другой подпрограммы, перегружать подпрограммы. А так же познакомились с таким понятием как рекурсия.


Задачи.

1. В параграфе приведена программа Sortirovka_massiva. Переделать программу, организовав сортировку одномерного массива с помощью рекурсии.

2. Написать программу сортировки массивов целых чисел, вещественных чисел и строк длиной в 5 символов. В программе должны быть  перегруженная процедура перестановки значений элементов массива. Так как мы ещё не умеем передавать массивы в качестве параметров, то для сортировки и вывода массивов можно написать процедуры с разными именами или организовать сортировки в самой программе. Сортировку организовать или с помощью рекурсии или с помощью цикла на ваше усмотрение. Выводить на экран каждый шаг сортировки не надо. Массивы сформировать случайным образом. Продемонстрировать на экране массивы до и после сортировки.


Решение.

1.

Program sortirovka_massiva_recursiya;

Var m:array [1..10]of integer;

    shag:integer;

Procedure Vivod;

begin

  for var i:=1 to 10 do

    begin

     write(m[i]:4,'  ');

     if (i mod 5) = 0 then writeln;

    end;

  writeln;

end;


Procedure perestanovka(var x,y:integer);

begin

  var temp:=x;

  x:=y;

  y:=temp;

end;


Procedure sortirovka;

begin

  for var i:=1 to 9 do

    if m[i]>m[i+1] then

      begin

        perestanovka(m[i],m[i+1]);

        inc(shag);

        Writeln(shag,'-й шаг сортировки');

        vivod;

        sortirovka;

      end

end;


begin

  shag:=0;

  for var i:=1 to 10 do m[i]:= random(0,100);

  writeln('Имеем следующий список случайных чисел:');

  vivod;

  writeln('Начинаем сортировку');

  sortirovka;

  Writeln('После сортировки список стал следующим:');

  vivod;

end.

_________________________________________________________

Имеем следующий список случайных чисел:

   7    46    21    22    91 

   3     3    80    23    99 


Начинаем сортировку

1-й шаг сортировки

   7    21    46    22    91 

   3     3    80    23    99 


2-й шаг сортировки

   7    21    22    46    91 

   3     3    80    23    99 

______________________________________________________

......................................................

______________________________________________________


15-й шаг сортировки

   3     3     7    21    22 

  46    23    80    91    99 


16-й шаг сортировки

   3     3     7    21    22 

  23    46    80    91    99 


После сортировки список стал следующим:

   3     3     7    21    22 

  23    46    80    91    99 


2. В целях экономии бумаги решение данной задачи не привожу, т.к. данную программу можно написать на основе предыдущей. Оставляю эту задачу на вашу самостоятельную работу.




Предыдущий параграф Назад в содержание Следующий параграф