способы передачи параметров в подпрограмму
Способы передачи параметров в подпрограмму
При первом способе (передача по значению) значения фактических параметров копируются в соответствующие формальные параметры. При изменении этих значений в ходе выполнения процедуры (функции) исходные данные (фактические параметры) измениться не могут. Поэтому таким способом передают данные только из вызывающего блока в подпрограмму (т.е. входные параметры). При этом в качестве фактических параметров можно использовать и константы, и переменные, и выражения.
При втором способе (передача по ссылке) все изменения, происходящие в теле процедуры (функции) с формальными параметрами, приводят к немедленным аналогичным изменениям соответствующих им фактических параметров. Изменения происходят с переменными вызывающего блока, поэтому по ссылке передаются выходные параметры. При вызове соответствующие им фактические параметры могут быть только переменными.
Локальные и глобальные идентификаторы
Использование процедур и функций в Паскале тесно связано с некоторыми особенностями работы с идентификаторами (именами) в программе. В часности, не все имена всегда доступны для использования. Доступ к идентификатору в конкретный момент времени определяется тем, в каком блоке он описан.
Имена, описанные в заголовке или разделе описаний процедуры или функции называют локальными для этого блока. Имена, описанные в блоке, соответствующем всей программе, называют глобальными. Следует помнить, что формальные параметры прцедур и функций всегда являются локальными переменными для соответствующих блоков.
Глобальные имена хранятся в области памяти, называемой сегментом данных (статическим сегментом) программы. Они создаются на этапе компиляции и действительны на все время работы программы.
В отличие от них, локальные переменные хранятся в специальной области памяти, которая называется стек. Они являются временными, так как создаются в момент входа в подпрограмму и уничтожаются при выходе из нее.
Имя, описанное в блоке, «закрывает» совпадающие с ним имена из блоков, содержащие данный. Это означает, что если в двух блоках, один из которых содержится внутри другого, есть переменные с одинаковыми именами, то после входа во вложенный блок работа будет идти с локальной для данного блока переменной. Пременная с тем же имнем, описанная в объемлющем блоке, становится временно недоступной и это продолжается до момента выхода из вложенного блока.
Рекомендуется все имена, которые имеют в подпрограммах чисто внутреннее, вспомогательное назначение, делать локальными. Это предохраняет от изменений глобальные объекты с такими же именами.
Волгоградский государственный педагогический университет
Кафедра алгебры, геометрии и информатики
Способы передачи параметров в подпрограммы
Существует два способа передачи параметров в подпрограммы – передача по значению и передача по наименованию. Способ передачи указывается при объявлении параметра в списке формальных параметров.
По умолчанию предполагается, что параметры обычных типов, например, Real, Integer, String, передаются по значению, а параметры таких типов как визуальные компоненты, динамические массивы передаются по наименованию. Если возникает необходимость явно указать, что параметр передается по наименованию, то перед именем параметра, или перечнем имен одного типа, пишется слово var.
3.1.2.1 Передача параметров по значению
Передача параметров по значению предполагает, что при вызове подпрограммы в памяти будет выделена специальная область для записи копий значений фактических параметров, с которыми и будет работать подпрограмма.
Такой способ передачи защищает переменные, передаваемые в подпрограмму в качестве параметров, от непредусмотренного изменения, так как подпрограмма работает с копиями. Кроме того, такой способ позволяет в качестве фактических параметров задавать выражения. При передаче параметров будет вычислено значение этого выражения и передано в подпрограмму.
Недостаток такого способа передачи состоит в том, что при передаче параметров, требующих много памяти, например, большие массивы чисел или длинные строки, копии занимают много места в памяти и требуется много времени для пересылки данных из одного места памяти в другое.
3.1.2.2 Передача параметров по наименованию
При передаче параметров по наименованию в подпрограмму передаются адреса фактических параметров. Поэтому такой способ передачи называется еще передачей параметров по адресу или по ссылке.
При таком способе передачи в качестве фактических параметров можно передавать только переменные. Выражение и даже отдельное число или символ передать по наименованию нельзя.
Передача параметров по наименованию экономит память и сокращает время обращения к подпрограммам. Однако это имеет и побочный эффект. Так как подпрограмма работает непосредственно с фактическими параметрами, то любое изменение формального параметра является изменением фактического параметра. Для исключения такого эффекта нужно в объявлении формальных параметров вместо слова var использовать слово const.
Но побочный эффект имеет и положительную сторону. Передачу параметров по наименованию можно использовать для возвращения результатов работы подпрограммы через фактические параметры. Такой способ возвращения особенно эффективен, когда требуется вернуть несколько параметров. Ведь функция возвращает только одно значение.
Выше, в пункте 3.1.1 уже рассматривался пример процедуры с передачей параметра по наименованию.
Рассмотрим еще один пример использования передачи параметров по наименованию для возвращения результатов работы подпрограммы.
В создаваемом ниже приложении для расчетов по формуле используется процедура считывания коэффициентов уравнения. Исходный текст этой процедуры приведен ниже.
ProceduregetKoef (var kA, kB, kC:Real);
В этой процедуре содержимое текстовых полей преобразуется в вещественные числа, и результаты преобразования присваиваются формальным параметрам, которые объявлены как передаваемые по наименованию. На это указывает слово varперед именами параметров.
Обращение к этой процедуре происходит в другой процедуре, которая обрабатывает событие onClick кнопки «Расчет по формуле». Фрагмент этой процедуры приведен ниже.
До вызова процедуры значения переменных a, b, c были неопределенными (мусор). При вызове процедуры getKoef адреса этих переменных были переданы в качестве фактических параметров. При выполнении процедуры, по этим адресам были записаны результаты преобразования данных. В результате, после окончания работы процедуры getKoef переменные a, b, c приобрели новые значения, которые были вычислены в процедуре
procedure TfrmSubroutine.btnCalculateRootsClick(Sender: TObject);
// До вызова процедуры getKoefKvUr
// значения переменных a,b,c не определены
//После вызова процедуры значения переменных
// a,b,c соответствуют содержиммому тектовых полей
Передача параметров в функцию по значению. Параметры функции. Передача аргументов в функцию. #34
Передача параметров в подпрограмму.
Обмен информацией между вызываемой и вызывающей функциями осуществляется с помощью механизма передачи параметров.
Переменные, указанные в заголовке подпрограммы называются формальными параметрами или просто параметрами подпрограммы. Эти переменные могут использоваться внутри подпрограммы. Список переменных в операторе вызова подпрограммы – это фактические параметры, или аргументы.
Механизм передачи параметров обеспечивает обмен данных между формальными и фактическими параметрами, что позволяет выполнять подпрограмму с различными данными. Между фактическими параметрами в операторе вызова и формальными параметрами в заголовке подпрограммы устанавливается взаимно однозначное соответствие. Количество, типы и порядок следования формальных и фактических параметров должны совпадать.
Передача параметров выполняется следующим образом. Вычисляются выражения, стоящие на месте фактических параметров. В памяти выделяется место под формальные параметры в соответствии с их типами. Выполняется проверка типов и при их несоответствии выдается диагностическое сообщение. Если количество и типы формальных и фактических параметров совпадают, то начинает работать механизм передачи данных между фактическими и формальными параметрами.
Формальные параметры процедуры можно разделить на два класса: параметры-значения и параметры-переменные.
При передаче данных через параметры-значения в подпрограмму передаются значения фактических параметров, и доступа к самим фактическим параметрам из подпрограммы нет.
При передаче данных параметры-переменные заменяют формальные параметры, и, следовательно, в подпрограмме есть доступ к значениям фактических параметров. Любое изменение параметров переменных в подпрограмме приводит к изменению соответствующих им формальных параметров. Следовательно, входные данные следует передавать через параметры-значения, для передачи изменяемых в результате работы подпрограммы данных следует использовать параметры-переменные.
Процедуры.
Описание процедуры имеет вид:
Начинается описание с заголовка процедуры, где procedure – ключевое слово языка, имя_процедуры – любой допустимый в языке Free Pasacal идентификатор, формальные_параметры – имена формальных параметров и их типы, разделенные точкой с запятой.
Рассмотрим примеры заголовков процедур с параметрами-значениями:
procedure name_1(r:real; i:integer; c:char);
Однотипные параметры могут быть перечислены через запятую:
procedure name_2(a,b:real; i,j,k:integer);
Список формальных параметров не обязателен и может отсутствовать:
Если в заголовке процедуры будут применяться параметры-переменные, то перед ними необходимо указывать служебное слово var, перед параметрами-значениями слово var отсутствует:
procedure name_4(x,y:real; var z:real);
//x, y – параметры-значения,
После заголовка идет тело процедуры, которое состоит из раздела описаний (константы, типы, переменные, процедуры и функции, используемые в процедуре) и операторов языка, реализующих алгоритм процедуры.
Для обращения к процедуре необходимо использовать оператор вызова:
Фактические параметры в списке оператора вызова отделяются друг от друга запятой:
Если в описании процедуры формальные параметры отсутствовали, то и при вызове их быть не должно:
Функции.
Описание функции также состоит из заголовка и тела:
Примеры описания функций:
function fun_1 (x:real):real;
function fun_2(a, b:integer):real;
Тело функции состоит из раздела описаний (константы, типы, переменные, процедуры и функции, используемые в процедуре) и операторов языка, реализующих ее алгоритм. В теле функции всегда должен быть хотя бы один оператор, присваивающий значение имени функции.
function fun_2(a, b:integer):real;
Обращение к функции осуществляется по имени с указанием списка фактических параметров, разделенных запятой:
Приложение 2.
Массивы.
Массив – структурированный тип данных, состоящий из фиксированного числа элементов одного типа.
Массив, представляющий собой просто список данных одного и того же типа, называют простым, или одномерным массивом. Для доступа к данным, хранящимся в определенном элементе массива, необходимо указать имя массива и порядковый номер этого элемента, называемый индексом.
Если возникает необходимость хранения данных в виде таблиц, в формате строк и столбцов, то необходимо использовать многомерные массивы.
Например массив, состоящего из строк и столбцов. Это двумерный массив. Строки в нем можно считать первым измерением, а столбцы — вторым. Для доступа к данным, хранящимся в этом массиве, необходимо указать имя массива и два индекса, первый должен соответствовать номеру строки, а второй — номеру столбца, в которых хранится необходимый элемент.
Описание массивов
Для описания массива служат служебные слова array of. Описать массив можно двумя способами:
Ввести новый тип данных, а потом описать переменные нового типа. В этом случае формат оператора type следующий:
имя_типа = array [тип_индекса] of тип_компонентов;
В качестве типа_индекса следует использовать перечислимый тип. Тип_компонентов — это любой ранее определенный тип данных, например:
massiv=array[0..12] of real;
//Тип данных massiv из 13 элементов, элементы нумеруются от 0 до 12.
dabc=array[-3..6] of integer;
Можно не вводить новый тип, а просто описать переменную следующим образом:
переменная: array [тип_индекса] of тип_переменной;
z,x: array[1..25] of word;
//Массивы z и x из 25 значений типа word,
//элементы нумеруются от 1 до 25.
Для описания массива можно использовать предварительно определенные константы:
Константы должны быть определены до использования, так как массив не может быть переменной длины!
Двумерный массив (матрицу) можно описать, применив в качестве базового типа (типа компонентов) одномерный:
massiv=array[1..200] of real;
matrica=array[1..300] of massiv;
Такая же структура получается при использовании другой формы записи:
matrica = array [1..300,1..200] of real;
var ab:array [1..300,1..200] of real;
При всех трех определениях мы получали матрицу вещественных чисел, состоящую из 300 строк и 200 столбцов.
Аналогично можно ввести трехмерный массив, или массив большего числа измерений:
Сейчас онлайн:
На сайте — 14
На IRC-канале — 2
Подпрограммы (часть 3)
Решение домашнего задания из урока №20
Домашнее задание, предложенное в прошлом уроке было следующим:
Введение
Третий и завершающий урок по теме «подпрограммы». В предыдущих двух мы разобрались, что такое подпрограммы, зачем они нужны, а также научились создавать свои собственные подпрограммы и пользоваться ими. В этом уроке будет скорее не новый материал, а дополнение к уже имеющемуся.
Способы передачи параметров в подпрограммы
Тип данных Word выбран специально для того, чтобы отрицательные значения не могли быть переданы процедуре.
Проверка работы подпрограммы при нажатии на кнопку:
На один параметр больше.
Параметры-константы
Есть ещё одна модификация параметров подпрограммы. Можно любой из них сделать константой. Нужно это в случае, если вы хотите контроллировать свой код на предмет отсутствия команд для изменения значения входного параметра. Делается это добавлением слова const перед переменной в заголовке.
Например, мы хотим для нашей предыдущей задачи находить x и y не для числа n, а для n+1. Для этого добавим в тело подпрограммы соответствующую строку:
Но теперь, если мы сделаем параметр n константой, компилятор выдаст ошибку на только что добавленной строке:
Понятно, что при передаче по значению даже если значение параметра и будет изменено, во внешнюю программу оно не попадёт. Но для корректировки работы функций объявление параметров константами может быть полезным.
Необязательные параметры
Часто подпрограммы имеют большое количество параметров, что подчёркивает их универсальность и обширность выполняемых действий. Однако во многих случаях одни и те же параметры имеют одинаковое значение, т.е. среди вариантов значений одного параметра есть наиболее популярный. В этом случае для упрощения вызова подпрограмм часть параметров делают необязательными. Необязательный параметр может быть передан подпрограмме, а может и быть опущен. Для того случая, когда он опущен, имеется его исходное значение, которое подпрограмма и будет использовать.
Необязательные параметры указываются в квадратных скобках.
Удобно, согласитесь? Сейчас мы научимся делать необязательные параметры и в своих подпрограммах.
Пример. Создадим функцию, вычисляющую десятичный логарифм заданного числа или логарифм по указанному основанию.
Посмотрите количество параметров функции CreateProcess():
Привязка подпрограмм к форме
Итак, чтобы «привязать» процедуру к форме, мы должны сделать следующее:
Между именем класса формы и именем подпрограммы должна стоять точка. В результате заголовок процедуры станет таким:
А если, например, есть Form2 и CheckBox2 на ней, то обработчик будет такой:
Заключение
Автор: Ерёмин А.А.
Статья добавлена: 4 июня 2008
Зарегистрируйтесь/авторизируйтесь,
чтобы оценивать статьи.
Статьи, похожие по тематике
Для вставки ссылки на данную статью на другом сайте используйте следующий HTML-код:
Ссылка для форумов (BBCode):
Быстрая вставка ссылки на статью в сообщениях на сайте:
<
<<статья:121>> — полноценная HTML-ссылка на статью (текст ссылки — название статьи).
Поделитесь ссылкой в социальных сетях:
Комментарии читателей к данной статье
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
private
< Private declarations >
public
procedure TForm1.Button1Click(Sender: TObject);
procedure GetXY(n: Word; var x, y: Word);
begin
x:=Trunc(Sqrt(n));
y:=n-Sqr(x);
if y = 0 then
begin
Dec(x);
y:=n-Sqr(x);
end;
end;
var N,A,B: Word;
begin
N:=StrToInt(Edit1.Text);
GetXY(N,A,B);
Label1.Caption:=IntToStr(N)+’ = ‘+IntToStr(a)+’^2 + ‘+IntToStr(b);
end;
Задача №3 заработала:
unit Unit1;
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
Button1: TButton;
CheckBox3: TCheckBox;
procedure Button1Click(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
procedure CheckBox2Click(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;
var
Form1: TForm1;
A: Boolean; //»Пойдем на концерт! Я билет достал! (билет есть или врет)
B: Boolean; //»Ура! Я с тобой пойду! (пойдет на концерт или врет)
implementation
<$R *.dfm>
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
A:= CheckBox1.Checked
end;
procedure TForm1.CheckBox2Click(Sender: TObject);
begin
B:= CheckBox2.Checked
end;
Задача №2 заработала при:
unit Unit1;
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
Edit1: TEdit;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;
procedure TForm1.Button1Click(Sender: TObject);
function RusToUpper(Str: String): String;
const
LetL = ‘абвгдеёжзийклмнопрстуфхцчшщъыьэюя’;
LetU = ‘АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ’;
var i: Integer;
begin
Result:=Str;
for i:=1 to Length(Str) do
if Pos(Str[i],LetL) > 0 then
Result[i]:=LetU[Pos(Str[i],LetL)];
end;
var
N1: string;
begin
N1:=Edit1.Text;
Label1.Caption:=’заглавные и прописные кирилические символы написаны только как заглавные = ‘+RusToUpper(N1);
end;
Первое задание заработало при:
unit Unit1;
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
private
< Private declarations >
public
< Public declarations >
end;
procedure TForm1.Button1Click(Sender: TObject);
function Factorial(n: Integer): Int64;
var i: Integer;
begin
Result:=1;
for i:=2 to n do
Result:=Result*i;
end;
var n: Integer;
begin
n:=StrToInt(Edit1.Text);
Label1.Caption:=’Факториал обозначается как «n!» = 1*2*3*. *n = ‘+IntToStr(Factorial(n));
end;
Оставлять комментарии к статьям могут только зарегистрированные пользователи.
Способы передачи параметров в подпрограммы
Существует два способа передачи параметров в подпрограммы – передача по значению и передача по наименованию. Способ передачи указывается при объявлении параметра в списке формальных параметров.
По умолчанию предполагается, что параметры обычных типов, например, Real, Integer, String, передаются по значению, а параметры таких типов как визуальные компоненты, динамические массивы передаются по наименованию. Если возникает необходимость явно указать, что параметр передается по наименованию, то перед именем параметра, или перечнем имен одного типа, пишется слово var.
3.1.2.1 Передача параметров по значению
Передача параметров по значению предполагает, что при вызове подпрограммы в памяти будет выделена специальная область для записи копий значений фактических параметров, с которыми и будет работать подпрограмма.
Такой способ передачи защищает переменные, передаваемые в подпрограмму в качестве параметров, от непредусмотренного изменения, так как подпрограмма работает с копиями. Кроме того, такой способ позволяет в качестве фактических параметров задавать выражения. При передаче параметров будет вычислено значение этого выражения и передано в подпрограмму.
Недостаток такого способа передачи состоит в том, что при передаче параметров, требующих много памяти, например, большие массивы чисел или длинные строки, копии занимают много места в памяти и требуется много времени для пересылки данных из одного места памяти в другое.
3.1.2.2 Передача параметров по наименованию
При передаче параметров по наименованию в подпрограмму передаются адреса фактических параметров. Поэтому такой способ передачи называется еще передачей параметров по адресу или по ссылке.
При таком способе передачи в качестве фактических параметров можно передавать только переменные. Выражение и даже отдельное число или символ передать по наименованию нельзя.
Передача параметров по наименованию экономит память и сокращает время обращения к подпрограммам. Однако это имеет и побочный эффект. Так как подпрограмма работает непосредственно с фактическими параметрами, то любое изменение формального параметра является изменением фактического параметра. Для исключения такого эффекта нужно в объявлении формальных параметров вместо слова var использовать слово const.
Но побочный эффект имеет и положительную сторону. Передачу параметров по наименованию можно использовать для возвращения результатов работы подпрограммы через фактические параметры. Такой способ возвращения особенно эффективен, когда требуется вернуть несколько параметров. Ведь функция возвращает только одно значение.
Выше, в пункте 3.1.1 уже рассматривался пример процедуры с передачей параметра по наименованию.
Рассмотрим еще один пример использования передачи параметров по наименованию для возвращения результатов работы подпрограммы.
В создаваемом ниже приложении для расчетов по формуле используется процедура считывания коэффициентов уравнения. Исходный текст этой процедуры приведен ниже.
ProceduregetKoef (var kA, kB, kC:Real);
Begin
В этой процедуре содержимое текстовых полей преобразуется в вещественные числа, и результаты преобразования присваиваются формальным параметрам, которые объявлены как передаваемые по наименованию. На это указывает слово varперед именами параметров.
Обращение к этой процедуре происходит в другой процедуре, которая обрабатывает событие onClick кнопки «Расчет по формуле». Фрагмент этой процедуры приведен ниже.
До вызова процедуры значения переменных a, b, c были неопределенными (мусор). При вызове процедуры getKoef адреса этих переменных были переданы в качестве фактических параметров. При выполнении процедуры, по этим адресам были записаны результаты преобразования данных. В результате, после окончания работы процедуры getKoef переменные a, b, c приобрели новые значения, которые были вычислены в процедуре
procedure TfrmSubroutine.btnCalculateRootsClick(Sender: TObject);
var a, b, c :Real;
Begin
// До вызова процедуры getKoefKvUr
// значения переменных a,b,c не определены
//После вызова процедуры значения переменных
// a,b,c соответствуют содержиммому тектовых полей
Расположение подпрограмм
Подпрограммы могут располагаться в самых разных местах. Место расположения программы не влияет на ее работу, но от этого зависит доступность (видимость) подпрограммы. Ниже рассматриваются варианты расположения подпрограмм и обсуждаются особенности их использования, зависящие от расположения подпрограммы.
3.1.3.1 Подпрограммы обработки событий класса формы
В ранее рассмотренных примерах мы уже имели дело с такими подпрограммами.
Подпрограммы, относящимися к классу формы, объявляются в описании класса в разделе interface, а располагаются в разделе implementation модуля.
Отличительная особенность этих подпрограмм в том, что в разделе implementation их имя записывается вместе с именем класса, через точку. Это признак того, что подпрограмма принадлежит классу формы.
В таких подпрограммах доступны все компоненты, расположенные на форме. При обращении к ним, нет необходимости указывать имя формы, например, можно писать Edit1.text вместо Form1.Edit1.text.
Одна и та же процедура обработки события может быть связана с событиями нескольких объектов. В этих случаях, для того, чтобы получить доступ к объекту, вызвавшему процедуру, можно использовать ссылку на этот объект, которая передается в процедуру в виде параметра Sender. Чтобы получить доступ к объекту, нужно привести эту ссылку к соответствующему классу, например, таким образом – TButton(Sender).
3.1.3.2 Другие подпрограммы класса формы
Класс формы может содержать не только процедуры обработки событий, но и другие подпрограммы. Обычно эти подпрограммы как-то связаны с компонентами формы, и расположение подпрограммы в классе формы упрощает доступ к ее компонентам. Эти подпрограммы отличаются от процедур обработки событий только тем, что программист сам должен вводить объявления подпрограмм, кроме того, эти объявления могут располагаться только в разделах private или public описания класса формы. Сама же процедура будет размещаться в разделе implementation.
Рассмотрение подпрограмм, относящихся к классу формы, выходит за рамки нашего курса, поэтому мы, по возможности, не будем их использовать.
3.1.3.3 Размещение подпрограмм в модуле
В модуле формы, помимо подпрограмм, принадлежащих классу формы, могут располагаться и другие подпрограммы, причем, это не исключение, а скорее правило. В дальнейшем мы будем писать в основном именно такие подпрограммы.
Если такая подпрограмма используется только в данном модуле, то она описывается в разделе implementation,и ее описание должно располагаться раньше, чем обращение к ней.
В тех же случаях, когда подпрограмму предполагается использовать и в других модулях, то помимо описания в разделе implementation, ее следует объявить в разделе interface,но за пределами описания класса.Объявление подпрограммы модуля выглядит так же, как и ее заголовок.
Если в подпрограмме модуля, не принадлежащей к классу формы, приходится обращаться к компонентам формы, то при таких обращениях пред именем компонента следует указывать имя формы, например, Form1.Edit1.text.
3.1.3.4 Размещение подпрограмм в других модулях
Подпрограммы, которые могут использоваться в нескольких проектах, удобно не копировать в каждый из этих проектов, а размещать в отдельных модулях, которые можно подключать к проектам через инструкцию uses.
Мы уже видели, что часто используемые алгебраические и тригонометрические функции описаны в модуле System, другие математические процедуры описаны в модуле Math.
Описания таких процедур располагаются в разделе реализации модуля (implementation), но для того, чтобы процедура была доступна в других модулях, необходимо объявить процедуру в разделе interface, продублировав там заголовок процедуры.
3.1.3.5 Размещение подпрограмм внутри других подпрограмм
Как уже упоминалось выше, в разделе описаний подпрограммы могут быть описаны другие подпрограммы. Такие подпрограммы называются внутренними по отношению к подпрограмме, в которой они описаны. Сама процедура, по отношению к внутренним подпрограммам называется внешней.
Область действия имен
Под областью действия имени подразумевается та часть модуля или программы, в пределах которой это имя доступно программисту.
Если имя константы, переменной, подпрограммы или другого элемента программы объявлено в интерфейсной части модуля, то оно доступно не только в данном модуле, но и во всех других модулях, которые в инструкции uses имеют ссылку на данный модуль.
Если имя объявлено в разделе implementation модуля, то оно доступно только процедурам и функциям данного модуля.
Если имя объявлено в разделе описаний некоторой процедуры, то это имя доступно только в пределах данной процедуры. Такие имена иногда называют локальными. Достоинство локальных объявлений в том, что в различных процедурах могут использоваться одинаковые имена, и эти имена не будут конфликтовать.
Рисунок 3.2 – Вложенные процедуры
Подпрограммы могут быть вложенными, то есть в описательной части некоторой подпрограммы может быть описана еще одна подпрограмма, а в ней еще одна, и так далее. В таких случаях имя, описанное в некоторой процедуре, доступно не только в ней, но и во всех вложенных процедурах, но не доступно в подпрограммах более высокого уровня.
На рисунке 3.2 схематично показаны три процедуры. Процедура «с» описана в процедуре «b», которая, в свою очередь, описана в процедуре «а». Переменная «i», объявленная в процедуре «b», доступна и в процедуре «c», но недоступна (невидима) в процедуре «a».
3.2 Реализация проекта «Процедуры и функции»
В этом проекте мы будем решать ту же задачу, что и в проекте для предыдущей работы, но подход к решению задачи будет другой. Мы разобьем задачу на части, которые оформим в виде процедур и функций. Более того, эти составные части мы разместим в разных модулях.
Откройте новый проект. Удалите появившуюся форму и загрузите в проект модуль формы из проекта для предыдущей работы. Переименуйте модуль, дав ему имя, например UnitSubroutine. Слово Subroutine переводится с английского языка как подпрограмма. Теперь можно работать с этим модулем как с новым.
Форму переименуйте и назовите frmSubroutine. В заголовке напишите текст «Процедуры и функции».
Форма должна иметь вид подобный форме, изображенной на рисунке 3.3, но соответствовать варианту задания, выполненного в предыдущей работе.
Рисунок 3.3 – Интерфейс проекта «Процедуры и функции»
Создание процедур и функций
3.2.1.1 Процедуры инициализации
Процедуру очистки протокола можно оставить такой же, как в предыдущей работе, а так как модуль формы скопирован, то она у нас есть. В любом случае процедура будет соответствовать рисунку 3.4.
Рисунок 3.4 – Процедура очистки протокола
Что касается процедуры инициализации формы, то мы ее переделаем,
Прежде всего, создадим процедуру setKoef(), которая обеспечит установку начальных значений в поля ввода для переменных «а», «x» и «n», входящих в формулу. Параметрами, передаваемыми в процедуру, будут числовые значения этих переменных. В процедуре числа будут преобразовываться в строки, и заноситься в соответствующие компоненты TEdit. Такое преобразование целесообразно, так как в этом случае десятичный разделитель для дробных чисел будет установлен правильно при любой настройке системы.
Так как в этой процедуре мы будем обращаться к компонентам формы edtA, edtB, edtC, а процедура не принадлежит классу формы, то в ней перед именами компонент обязательно следует указывать имя формы, разделяя эти имена точкой.
Другой способ решения этой проблемы состоит в использовании конструкции with … do, с помощью которой имя формы выносится как бы за операторные скобки begin … end. Именно эта конструкция используется в процедуре setKoef(), показанной на рисунке 3.5.
Рисунок 3.5 – Процедура для инициализации полей ввода значений переменных, входящих в формулу
Теперь можно создать процедуру инициализации формы, связанную с событием onCreate. С помощью этой процедуры мы обеспечим очистку поля Memo и заполнение текстовых полей корректными исходными данными. Это ускорит отладку и проверку работоспособности программы.
В этой процедуре, представленной на рисунке 3.6, вначале имитируется нажатие кнопки «Очистить протокол», что приводит к вызову соответствующей процедуры.
Вторая строка тела процедуры вызывает процедуру setKoef(), в которую передаются значения переменных. Эти значения появятся в полях edtA, edtB, edtC.
Рисунок 3.6 – Процедура инициализации формы
3.2.1.2 Процедура считывания коэффициентов уравнения
Для расчета по формуле необходимо прочитать значения переменных из компонентов TEdit и преобразовать их в вещественные числа.
Возложим решение этой проблемы на специальную процедуру. Назовем процедуру getKoef. Параметрами процедуры будут три переменных вещественного типа, которые соответствуют переменным формулы. Так как эта процедура должна возвращать значения вышеуказанных параметров, то все они должны передаваться по наименованию, то есть, описаны в заголовке процедуры со словом var. Текст процедуры приведен ниже.
Рисунок 3.7 – Процедура считывания значений коэффициентов уравнения
3.2.1.3 Функция для расчета по формуле
В предыдущем проекте расчет по формуле, ввод исходных данных и вывод результатов были реализованы в одной процедуре и все эти операции были привязаны к форме. Однако это не совсем правильно, можно даже сказать, что совсем неправильно.
Дело в том, что расчет по формуле – это одна задача, а ввод исходных данных – совсем другая. Данные могут вводиться не только с формы, но и из файла, из массива или из какого-то другого источника данных. При этом расчет по формуле остается без изменений.
Кроме того, расчет по этой формуле может понадобиться и в других приложениях.
Вывод результатов – это тоже отдельная задача, которая может решаться различными способами.
Поэтому, для расчетов по формуле мы напишем функцию, которая не будет иметь отношения к форме.
Исходными данными для расчета по формуле являются вещественные числа a, x, n. Результатом – тоже вещественное число.
Описание функции для расчета по формуле может выглядеть так.
function myFormula1(a, x, n:real): real;
var ch, q, zn,
Begin
ch:=exp(x*ln(a))+sqr(sin(x)); //числитель формулы
q:=x*(n*a+a)/n/a; //подкоренное выражение
zn:= exp(ln(q)/n); //знаменатель формулы
Эту функцию для расчета по формуле поместим в отдельном модуле.
Добавьте новый модуль в проект, воспользовавшись функцией главного меню File→New→Unit.
В интерфейсной части модуля объявите функцию, написав ее заголовок, а в раздел реализации вставьте ее описание. В результате, модуль должен иметь вид, представленный на рисунке 3.8.
Сохраните модуль в той же папке, что и модуль формы проекта (хотя это и не обязательно), присвоив ему имя UnitDop.
Откомпилируйте модуль, вызвав функцию главного меню Project→CompilePtoject.
Посмотрите содержимое папки, где вы сохранили модуль. Там должен появиться файл UnitDop.dcu, который содержит откомпилированный модуль. Если это так, то модуль готов к употреблению.
Рисунок 3.8 – Функция расчета по формуле в дополнительном модуле
3.2.1.4 Использование дополнительного модуля
Для того, чтобы в основном модуле можно было воспользоваться функцией расположенной в дополнительном модуле, следует, прежде всего, дополнить инструкцию uses основного модуля, добавив туда ссылку на модуль UnitDop.
Обрабатывая инструкцию uses,компилятор дойдет до ссылки на модуль UnitDop и начнет искать файл UnitDop.dcu. Прежде всего, поиск будет производиться в папке, где расположен проект и форма, но если там модуля нет, то компилятору необходимо подсказать, где искать этот файл.
В этом случае ызовите диалог настройки путей, вызвав функцию главного меню Project→Options, откройте в нем закладку Directories/Conditionals, и в строке Search Path укажите путь к папке, где расположен модуль.
3.2.1.5 Завершение работы над проектом
Для проверки работоспособности процедуры ввода коэффициентов и функции расчета по формуле, описанной в дополнительном модуле, напишем процедуру обработки события onClick для кнопки «Выполнить расчет. Процедура представлена на рисунке 3.9.
Рисунок 3.9 – Главная процедура с вызовом вспомогательной процедуры и функции