Опубликован: 25.06.2014 | Уровень: для всех | Доступ: платный | ВУЗ: Учебный центр "ANIT Texno Inform"
Лекция 9:

Подпрограммы

< Лекция 8 || Лекция 9: 123 || Лекция 10 >
Аннотация: В данной лекции рассматривается работа с подпрограммами - процедурами и функциями. Подробно рассматриваются аргументы, передаваемые в подпрограммы, параметры по значению, параметры по ссылке. Рассматриваются различные способы реализации подпрограмм, а также такое важное понятие, как область видимости переменных и других объектов.

Цель лекции

Освоение работы с подпрограммами, с параметрами по ссылке, параметрами по значению, с досрочным выходом из программ и подпрограмм, с областью видимости переменных.

Подпрограммы

Вначале языки программирования были проще, они выполнялись строго сверху-вниз, один оператор за другим. Такие языки еще называли линейными. Типичный пример линейных языков - Бейсик. Единственную возможность организовать хоть какую-то логику в таких языках предоставлял оператор безусловного перехода 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);
    

Однако, разбавим теорию практикой, и поработаем с процедурами на реальном примере. Откройте Lazarus с новым проектом. Как всегда, назовем главную форму (свойство Name) fMain, сохраним проект в папку 09-01, при этом назовем проект, например, MyPodprog, а модулю дадим имя Main.

В свойстве Caption формы напишем

Примеры работы с подпрограммами
    

Наша задача: получить от пользователя вещественное число, удвоить его, и результат вывести на экран. Пользователь может ввести и целое число, но процедура обработает его как вещественное (помните о преобразовании типов в прошлой лекции?), например, если пользователь введет 3, то процедура получит 3.0. В результате вычисления получится 6.0, но FloatToStr() конечные нули не выводит, так что пользователь увидит на экране просто 6.

Ладно, сейчас нужно решить, как получить у пользователя число. Для этого используем компонент TEdit, который нам уже знаком по прошлым лекциям. Для начала установим метку TLabel с поясняющим текстом

Введите любое число:
    

а рядом установим TEdit. Имена у TLabel и TEdit оставим по умолчанию, TEdit будет называться Edit1. Не забудьте очистить у него свойство Text.

Ниже установите простую кнопку TButton, в Caption которой напишите текст:

Пример удвоения №1
    

Разумеется, будут и другие примеры. Подровняйте компоненты, при необходимости измените их размеры. Наша форма должна выглядеть примерно так:

Окно программы MyPodprog

Рис. 9.1. Окно программы MyPodprog

Пока что мы будем вынуждены доверять пользователю, что он введет в поле Edit1 число, и ничего более. Но на прошлой лекции вам было обещано показать реализацию "защиты от дураков", так что чуть позже мы и это сделаем.

Сгенерируйте событие нажатия на кнопку, оно будет таким:

procedure TfMain.Button1Click(Sender: TObject);
begin
  Udvoenie(Edit1.Text);
end;
    

Теперь нам нужно создать процедуру Udvoenie выше нашего события, сразу после комментария

{ TfMain }
    

Текст процедуры приведен выше.

Реализация подпрограммы Udvoenie

Рис. 9.2. Реализация подпрограммы Udvoenie

Обратите внимание, мы передаем в подпрограмму значение, которое ввел пользователь, и которое хранится в свойстве Text компонента Edit1:

Udvoenie(Edit1.Text);
    

Никаких дополнительных переменных в данном случае создавать не нужно. Сохраните проект и запустите его на выполнение. Попробуйте ввести целое число. Затем вещественное. Обратите внимание: если у вас установлена русская версия Windows, то в качестве разделителя вещественного числа нам нужно вводить запятую, а не точку! Помните про глобальную переменную DecimalSeparator?

Если же вы случайно или намеренно ввели точку, то выйдет сообщение об ошибке, подобное этому:

Сообщение об ошибке

Рис. 9.3. Сообщение об ошибке

Ничего страшного, нажмите кнопку "Останов", затем выберите команду главного меню "Запуск -> Сбросить отладчик". Lazarus закроет зависший проект, и вы сможете запустить его снова. Похожая ошибка возникнет, если вы попытаетесь удвоить пустую строку. Если же вы ввели числа правильно, то программа отработает как нужно в независимости, целое это было число, или вещественное. Не закрывайте пока проект, он нам еще понадобится.

< Лекция 8 || Лекция 9: 123 || Лекция 10 >
Инга Готфрид
Инга Готфрид
Александр Скрябнев
Александр Скрябнев

Через WMI, или используя утилиту wmic? А может есть еще какие более простые пути...