Подпрограммы Вначале языки программирования были проще, они выполнялись строго сверху-вниз, один оператор за другим. Такие языки еще называли линейными
Подпрограммы Вначале языки программирования были проще, они выполнялись строго сверху-вниз, один оператор за другим. Такие языки еще называли линейными. Типичный пример линейных языков - Бейсик. Единственную возможность организовать хоть какую-то логику в таких языках предоставлял оператор безусловного перехода GOTO, который в зависимости от условия, "перепрыгивал" на заранее расставленные метки. В современных языках программирования GOTO тоже остался, наверное, для любителей антиквариата. Но его применение может привести к трудно обнаруживаемым логическим ошибкам времени выполнения (run-time errors). Использование GOTO в современном программировании считается дурным тоном. Мы не будем изучать эту возможность, поскольку для организации логики есть куда более "продвинутые" средства! Одним из таких средств являются подпрограммы.
Подпрограмма - это часть кода, которую можно вызвать из любого места программы неопределенное количество раз. Другими словами, подпрограммы подобны строительным кирпичикам, из которых, в конце концов, получается здание - программа. Без подпрограмм можно обойтись, если вы пишете небольшую учебную программу на пару десятков строк кода. А если это серьезное приложение, с парой сотен модулей, в каждом из которых могут быть тысячи строк кода? Как такую программу написать, не разбивая задачу на отдельные части? Подпрограммы помогают улучшить код, структурировать его. Поэтому языки высокого уровня, которые позволяют использовать подпрограммы, называют ещё процедурно-ориентированными языками. И наш компилятор FPC тоже относится к таким языкам.
Подпрограммы бывают двух типов: процедуры и функции - первые просто выполняют свою работу, вторые еще и возвращают результат этой работы.
Процедуры
На самом деле, мы уже неоднократно использовали процедуры. Например, когда генерировали событие нажатия на кнопку. Это событие - процедура. Процедура начинается с ключевого слова procedure и имеет следующий синтаксис:
procedure <имя процедуры>(<список параметров>);
const
<объявление констант>;
type
<объявление новых типов>;
var
<объявление переменных>;
<описание вложенных процедур и функций>;
begin
<тело процедуры>;
end;
Большая часть указанного синтаксиса является необязательной - процедура может не иметь параметров, констант, пользовательских типов данных, переменных и т.п. - она может быть очень простой, например:
procedure ErrorMessage;
begin
ShowMessage('Ошибка!' +#13 + 'На ноль делить нельзя!');
end;
Такую процедуру можно вызвать из любого места программы, но процедура обязательно должна быть описана выше - ведь иначе компилятор не будет знать о ней. Есть еще возможность предварительно объявить процедуру, но об этом чуть позже. Итак, если эта процедура описана выше, то мы можем вызвать её, просто указав её имя:
ErrorMessage;
Компилятор перейдет к процедуре и выполнит её код (в данном случае - выведет сообщение об ошибке). После этого компилятор вернется назад, и выполнит следующий за вызовом процедуры оператор.
Параметры Параметры подпрограмм - достаточно важная тема, поговорим об этом подробней. В процедуру (или в функцию) можно передать какие то исходные данные, чтобы процедура их обработала. Такие данные называются параметрами, или формальными параметрами. Пример - пользователь ввел какое-то число (а на самом деле, строку из цифровых символов), нам нужно удвоить его, а результат сообщить пользователю. Описать подобную процедуру можно следующим образом:
procedure Udvoenie(st: string);
var
r: real;
begin
//полученную строку преобразуем в число:
r:= StrToFloat(st);
//теперь удвоим его:
r:= r * 2;
//теперь выведем результат в сообщении:
ShowMessage(FloatToStr(r));
end;
Этот пример уже сложнее, правда? На самом деле, всё просто. Давайте разберем, что тут к чему. Итак, строка объявления процедуры:
procedure Udvoenie(st: string);
объявляет процедуру Udvoenie с параметром строкового типа st. Это означает, что теперь мы можем вызвать процедуру, передав ей в качестве параметра какую-то строку. Параметр st условно можно считать внутренней переменной процедуры, в которую компилятор скопирует передаваемую процедуре строку. Этот способ передачи данных в подпрограмму называется параметром по значению. Допустим, в дальнейшем мы вызвали процедуру таким образом:
Udvoenie('123.4');
Компилятор сделает вызов процедуры, передав в параметр st указанное значение '123.4'. Или же мы можем вызвать процедуру иначе, передав в неё значение, которое хранится в какой то другой строковой переменной:
myst:= '123.4';
Udvoenie(myst);
Результат будет таким же. Тут важно помнить, что тип передаваемого значения обязательно должен совпадать с типом параметра. Если параметр у нас string, то и передавать ему нужно значение типа string. Компилятор копирует это значение в параметр. Другими словами, если внутри процедуры мы изменим значение параметра st, это никак не отразится на переменной myst, поскольку мы изменим копию данных, а не сами данные.
Пойдем дальше. А дальше мы объявляем вещественную переменную r:
var
r: real;
Здесь она необходима, ведь нам нужно умножить значение параметра на два, поэтому мы вынуждены будем преобразовать строковое представление числа в настоящее число - ведь строку на два не умножишь! Результат поместим в r:
begin
//полученную строку преобразуем в число:
r:= StrToFloat(st);
Служебным словом begin мы начинаем тело процедуры. Стандартной функцией StrToFloat(st) мы преобразуем строковое значение параметра st в число, и присвоим это число переменной r.
Далее всё просто:
//теперь удвоим его:
r:= r * 2;
//теперь выведем результат в сообщении:
ShowMessage(FloatToStr(r));
end;
Мы удваиваем значение r, результат этого помещаем снова в r, затем стандартной функцией FloatToStr(r) преобразуем полученное число в строку, и выводим эту строку в сообщении ShowMessage(). Вот, собственно, и всё.
Теперь мы сможем вызывать эту процедуру, когда необходимо, и передавать ей различные числа в виде строки. А уж функция сама позаботится обо всех необходимых преобразованиях, об удвоении числа и выводе результатов на экран.
Кстати, сами данные, которые мы передаём в подпрограмму, называются аргументами или фактическими параметрами. В примере вызова процедуры
myst:= '123.4';
Udvoenie(myst);
переменная myst - аргумент.
В качестве параметров в процедуре можно использовать не одну, а множество переменных. Если они имеют одинаковый тип, то их имена разделяют запятыми, а тип указывается в конце сразу для всех параметров. Например:
procedure MyStrings(st1, st2, st3: string);
Если параметры имеют разные типы, их разделяют точкой с запятой:
procedure MyProc1(st: string; r1:real);
procedure MyProc2(st1, st2, st3:string; r1:real);