|
.: Учебник по Delphi 7 для начинающих :.
Глава
6 Процедуры
и функции
Часто, работая
над программой, программист замечает, что некоторая последовательность инструкций
встречается в разных частях программы несколько раз. Например, в листинге 6.1
приведена программа пересчета веса из фунтов в килограммы. Обратите внимание,
что инструкции, обеспечивающие ввод исходных данных из полей редактирования,
расчет и вывод результата (в листинге они выделены фоном), есть как в процедуре
обработки события на кнопке Вычислить, так и в процедуре обработки события
OnKeyPress В поле Editl.
Листинг
6.1. Пересчет веса из фунтов в килограммы
unit
Unitl;
interface
uses Windows, Messages, SysUtils, Variants,
Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Label1: TLabel;
// пояснительный текст
Edit1: TEdit;
// поле ввода веса в фунтах
Button1: TButton;
// кнопка Вычислить
Label2: TLabel;
// поле вывода результата
procedure
ButtonlClick(Sender: TObject); procedure EditlKeyPress(Sender: TObject;
var Key:
Char); private
{ Private
declarations } public
{ Public
declarations }
end;
var
Form1: TForm1
;
implementation
{$R *.dfm}
// щелчок
на кнопке Вычислить
procedure
TForml.Button1Click(Sender: TObject);
var
f : real; //
вес в фунтах kg : real; // вес в килограммах
begin
f := StrToFloat(Edit1.Text);
kg := f; * 0.4059;
Label2.Caption
:= Edit1.Text + ' ф. — это ' +
FloatToStrF(kg,
ffGeneral, 4, 2} + 'кг.'; end;
// нажатие
клавиши в поле ввода исходных данных
procedure
TForml.Edit1KeyPress(Sender: TObject; var Key: Char);
var f : real; // вес в фунтах kg : real; // вес в килограммах
begin if Key = Char(VK_RETURN) then
begin
f: = . StrToFloat(Editl.Text)
;
kg := f * 0.4059;
Label2.Caption
:= Editl.Text + ' ф. - это ' + FloatToStrF(kg, ffGeneral, 4, 2) + 'кг.'1.; end;
end; end.
Можно избежать
дублирования кода в программе. Для этого надо оформить инструкции, которые встречаются
в программе несколько раз, как подпрограмму,
и заменить инструкции, оформленные в виде подпрограммы, инструкцией вызова подпрограммы.
В листинге
6.2 приведена программа пересчета веса из фунтов в килограммы, в которой ввод
исходных данных, вычисления и вывод результата объединены в подпрограмму, реализованную
как функция. Листинг
6.2. Пересчет веса из фунтов в килограммы (использование процедуры)
unit
Onit1; interface
uses Windows, Messages, SysUtils, Variants,
Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1= class(TForm)
Label1: TLabel;
// пояснительный текст
Edit1: TEdit;
// поле ввода веса в фунтах
Button1: TButton;
// кнопка Вычислить
Label2: TLabel;
// поле вывела результата
procedure
Button1Click(Sender: TObject); procedure EditlKeyPress(Sender: TObject;
var Key:
Char); private
{ Private
declarations } public
{ Public
declarations } end;
var
Form1: TForm1;
implementation
{$R *.dfm} // процедура программиста
procedure
FuntToKg;
var
f : real; //
вес в фунтах kg : real; // вес в килограммах
begin
f := StrToFloat(Form1.Edit1.Text);
kg := f * 0.4059;
Forml.Label2.Caption
:= Forml.Edit1.Text + ' ф. — это ' + FloatToStrF(kg, ffGeneral, 4, 2) + 'кг.';
end;
// щелчок
на кнопке Вычислить
procedure
TForml.ButtonlClick(Sender: TObject);
begin
FuntToKg; //
вызов процедуры FuntToKg end;
// нажатие
клавиши в поле ввода исходных данных procedure TForm1.EditlKeyPress(Sender: TObject;
var Key:
Char);
begin
if Key
= Char(VK_RETURN)
then FuntToKg;
// вызов процедуры FuntToKg end; end.
Преимущества
использования подпрограмм очевидны. Во-первых, в программе нет дублирования
кода, что сокращает трудоемкость создания программы, делает более удобным процесс
отладки и внесения изменений. Представьте, что нужно изменить пояснительный
текст, выводимый программой пересчета веса из фунтов в килограммы. В программе,
не использующей подпрограмму, нужно просмотреть весь текст и сделать необходимые
изменения. Если программа использует подпрограмму, то изменения надо внести
только в текст подпрограммы. Во-вторых, значительно повышается надежность программы.
Следует обратить внимание, что подпрограммы используют не только тогда, когда
нужно избежать дублирования кода. Удобно большую задачу разделить на несколько
подзадач и оформить каждую задачу как подпрограмму. В этом случае значительно
улучшается "читаемость" программы и, как следствие, существенно облегчается
процесс отладки.
Подпрограмма
— это небольшая программа, которая решает часть общей задачи. В языке Delphi
есть два вида подпрограмм — процедура и функция.
У каждой
подпрограммы есть имя, которое используется в программе для вызова подпрограммы
(процедуры).
Отличие функции
от процедуры состоит в том, что с именем функции связано значение, поэтому функцию
можно использовать в качестве операнда выражения, например, инструкции присваивания.
Как правило,
подпрограмма имеет параметры. Различают формальные и фактические параметры.
Параметры,
которые указываются в объявлении функции, называются формальными. Параметры,
которые указываются в инструкции вызова процедуры, называются фактическими.
Параметры
используются:
В общем случае
в качестве фактического параметра процедуры можно использовать выражение, тип
которого должен совпадать с типом соответствующего формального параметра.
Функция
— это подпрограмма, т. е. последовательность инструкций, имеющая имя.
Процесс перехода
к инструкциям функции называется вызовом функции или обращением к функции. Процесс
перехода от инструкций функции к инструкциям программы, вызвавшей функцию, называется
возвратом из функции.
В общем виде
инструкция обращения к функции выглядит так:
Переменная
:= Функция (Параметры) ;
где:
Следует обратить
внимание на то, что:
Объявление функции в общем виде выглядит так:
function
Имя (параметр1 : тип1, ...,
параметрК : типК) : Тип; var
// здесь объявления
локальных переменных begin // здесь инструкции
функции Имя := Выражение;
end;
где:
Следует обратить
внимание, что последовательность инструкций, реализующих функцию, завершается
инструкцией, которая присваивает значение имени функции. Тип выражения, определяющего
значение функции, должен совпадать с типом функции, указанным в ее объявлении.
В качестве
примера в листинге 6.3 приведены функции isint и isFioat. Функция isint проверяет,
является ли символ, соответствующий клавише, нажатой во время ввода целого числа
в поле редактирования, допустимым. Предполагается, что допустимыми являются
цифры, клавиши <Enter> и <Backspace>. Функция IsFloat решает аналогичную
задачу, но для дробного числа. У функции IsFloat два параметра: код нажатой
клавиши и строка символов, которая уже введена в поле редактирования. Листинг
6.3. Примеры функций
// проверяет,
является ли символ допустимым
// во время
ввода целого числа
function
Islnt(ch : char) : Boolean;
begin
if (ch
>= '0'} and (ch <= '9') // цифры
or (ch
= 113) // клавиша <Enter>
or (ch
= #8) // клавиша <Backspace> then Islnt := True // символ допустим
else Islnt
:= False; // недопустимый символ
end;
// проверяет,
является ли символ допустимым
// во время
ввода дробного числа
function
IsFloat(ch : char; st: string) : Boolean;
begin
if (ch
>= '0') and (ch <= '9') // цифры
or (ch
= #13) // клавиша <Enter>
or (ch
= #8) // клавиша <Backspace>
then
begin IsFloat := True; // символ верный
Exit; // выход
из функции end;
case ch
of '-': if Length(st) = 0 then IsFloat := True; ',':
if (Pos(',',st)
= 0)
and (st[Length(st)]'>=
'0') and (st[Length(st)] <= '9')
then //
разделитель можно ввести только после цифры // и если он еще не введен
IsFloat := True;
else // остальные символы запрещены IsFloat := False; end; end;
Если вы собираетесь
использовать в программе свою функцию, то в простейшем случае ее объявление
следует поместить в текст программы, перед подпрограммой, которая применяет
эту функцию.
Рис.
6.1. Окно программы Поездка на дачу
Следующая
программа (ее текст приведен в листинге 6.4, а вид диалогового окна на
рис. 6.1) вычисляет стоимость поездки на дачу. Исходными данными для программы
являются: расстояние, цена одного литра бензина и потребление бензина на 100
км пути. Для ввода исходных данных применяются поля Edit1, Edit2 и Edit3. Функции
обработки события OnKeyPress
используют
функцию IsFioat для фильтрации вводимых в эти поля символов, во время работы
программы в полях ввода отображаются только допустимые символы. Листинг
6.4. Пример использования функций программиста
unit
fazenda_;
interface Windows, Messages, SysUtils, Variants,
Classes, Graphics,
Controls, Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
// расстояние
Edit2: TEdit;
// цена литра бензина
Edit3: TEdit;
// потребление бензина на 100 км
CheckBox1: TCheckBox;
// True - поездка туда и обратно
Button1: TButton;
// кнопка Вычислить
Label4: TLabel;
// поле вывода результата расчета
Label1: TLabel;
Label2: TLabel;
Label3: TLabel; procedure EditlKeyPress(Sender: TObject;
var Key:
Char); procedure Edit2KeyPress(Sender: TObject;
var Key:
Char); procedure Edit3KeyPress(Sender: TObject;
var Key:
Char); procedure Button1Click(Sender: TObject);
private { Private declarations }
public { Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// проверяет,
является ли символ допустимым
// во время
ввода дробного числа
function
IsFloat(ch : char; st: string) : Boolean;
begin
if (ch >=
'0') and (ch <= '9') // цифры
or (ch = #13)
// клавиша <Enter>
or (ch = #8)
// клавиша <Backspace>
then
begin IsFloat := True; // символ верный
Exit; // выход
из функции
end; case
ch of '-': if Length(st) = 0 then IsFloat := True; ', ':
if (Pos(',',st)
= 0)
and (st[Length(st)]
>= '0') and (st[Length(st)] <= '9') then // разделитель можно ввести только после цифры
// и если
он еще не введен
IsFloat := True/else
// остальные символы запрещены IsFloat := False; end;
end;
// нажатие
клавиши в поле Расстояние procedure TForm1.EditlKeyPress(Sender: TObject;
var Key:
Char);
begin
if Key
= Char(VK_RETURN) then Edit2.SetFocus // переместить курсор в поле Цена else
If not IsFloat(Key,Edit2.Text)
then Key := Chr(O);
end;
// нажатие
клавиши в поле Цена procedure TForml.Edit2KeyPress(Sender: TObject;
var Key:
Char);
begin
if Key
= Char(VK_RETURN) then Edit3.SetFocus // переместить курсор в поле Потребление else If not IsFloat(Key,Edit2.Text)
then Key
:= Chr (0);
end;
// нажатие
клавиши в поле Потребление procedure TForml.Edit3KeyPress(Sender: TObject;
var Key:
Char);
begin
if Key
= Char(VK_RETURN) then Buttonl.SetFocus // // сделать активной кнопку Вычислить
else If
not IsFloat(Key,Edit2.Text) then Key := Chr (0);
end;
// щелчок
на кнопке Вычислить
procedure
TForm1.ButtonlClick(Sender: TObject);
var
rast : real;
// расстояние
cena : real;
// цена
potr : real;
// потребление на 100 км
summ : real;
// сумма mes: string;
begin
rast := StrToFloat(Edit1.Text);
cena := StrToFloat(Edit2.Text);
potr := StrToFloat(Edit3.Text);
summ := rast
/ 100 * potr * cena;
if CheckBoxl.Checked
then summ := summ * 2;
mes := 'Поездка
на дачу';
if CheckBoxl.Checked
then mes := mes + ' и обратно'; mes := mes + 'обойдется в '
+ FloatToStrF(summ,ffGeneral,4,2)
+ ' руб.'; Label4.Caption := mes; end; end.
Процедура
— это разновидность подпрограммы. Обычно подпрограмма реализуется как процедура
в двух случаях:
В общем
виде объявление процедуры выглядит так: procedure Имя (var параметр!:
тип!; ... var параметрК: типК) ; var // здесь объявление локальных переменных
begin // здесь инструкции процедуры
end;
где:
Параметры
процедуры используются для передачи данных в процедуру, а также для возврата
данных из процедуры в вызвавшую ее программу.
В качестве
примера в листинге 6.5 приведена процедура решения квадратного уравнения (которое
в общем виде записывается так: ах2 + Ьх+ с = 0). У процедуры
шесть параметров: первые три предназначены для передачи в процедуру исходных
данных — коэффициентов уравнения; параметры xi и х2 используются для возврата
результата — корней уравнения; параметр ok служит для передачи информации о
том, что решение существует.
Листинг
6.5. Процедура SgRoot
// решает
квадратное уравнение procedure SqRoot(a,b,c : real; var xl,x2 : real; var ok : boolean); { a,b,c — коэффициенты уравнения x1,x2 — корни уравнения ok = True — решение есть ok = False — решения нет }
var d : real; // дискриминант
begin
d:= Sqr(b) -
4*a*c; if d < 0 then ok := False // уравнение не имеет решения
else
begin
ok := True; x1 := (-b + Sqrt(d)) / (2*a) ; x2 := (b + Sqrt(d)) / (2*a); end;
end;
Разработанную
процедуру нужно поместить в раздел implementation, перед подпрограммой, которая
использует эту процедуру.
Инструкция
вызова процедуры в общем виде выглядит так:
Имя(СписокПараметров);
где:
П имя
— имя вызываемой процедуры;
Фактическим
параметром, в зависимости от описания формального параметра в объявлении процедуры,
может быть переменная, выражение или константа соответствующего типа.
Например,
инструкция вызова приведенной выше процедуры решения квадратного уравнения может
выглядеть следующим образом: SqRoot(StrToFloat(Edit1.Text), StrToFloat(Edit2.Text), StrToFloat(Edit3.Text),
k1,k2,rez);
Если в описании
процедуры перед именем параметра стоит слово var, то при вызове процедуры на
месте соответствующего параметра должна стоять переменная основной программы.
Использование константы или выражения считается ошибкой, и компилятор в этом
случае выведет сообщение: Types of actual and formal var parameters must be
identical (ТИП фактического параметра должен соответствовать типу формального
параметра).
В листинге
6.6 приведена программа решения квадратного уравнения, в которой используется
процедура SqRoot. Окно программы представлено на рис. 6.2.
Рис.
6.2. Окно программы Квадратное уравнение
Листинг
6.6. Решение квадратного уравнения (использование процедуры)
unit
SqRoot_; interface
uses Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForml = class(TForm)
Editl: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Label1: TLabe1;
Label2: TLabe1;
Label3: TLabe1;
Label4: TLabe1;
Button1: TButton;
Label5: TLabel;
procedure
ButtonlClick(Sender: TObject); private { Private declarations }
public { Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// решает
квадратное уравнение procedure SqRoot(a,b,c : real; var xl, x2 : real; var ok : boolean);
{ a,b,c —
коэффициенты уравнения x1,x2 — корни уравнения ok = True — решение есть ok = False — решения нет }
var
d : real; //
дискриминант begin
d:= Sqr(b) -
4*a*c; if d < 0 then ok := False // уравнение не имеет решения
else
begin
ok := True; xl := (-b + Sqrt(d)) / (2*a); x2 := (b + Sqrt(d)) / (2*a) ; end;
end; procedure TForml.ButtonlClick(Sender: TObject);
var
k1,k2: real;
// корни уравнения rez: boolean; // True —решение есть, False —решения нет mes:
string; //
сообщение begin
SqRoot(StrToFloat(Editl.Text),
StrToFloat(Edit2.Text) , StrToFloat(Edit3.Text) , k1,k2,rez);
if rez
then
mes := 'Корни
уравнения' + #13 + 'x1='+FloatToStrF(kl,ffGeneral,
4,2)+#13+ 'x2='+FloatToStrF(k2,ffGeneral,4,2)+#13
else mes := 'Уравнение не имеет решения'; labels.Caption := mes;
end; end.
Повторное
использование функций и процедур
Разработав
некоторую функцию, программист может использовать ее в другой программе, поместив
текст этой функции в раздел implementation. Однако этот способ неудобен, т.
к. приходится набирать текст функции заново или копировать его из текста другой
программы.
Delphi позволяет
программисту поместить свои функции и процедуры в отдельный модуль, а затем
использовать процедуры и функции модуля в своих программах, указав имя модуля
в списке модулей, необходимых программе (инструкция uses).
Чтобы приступить
к созданию модуля, нужно сначала закрыть окно формы и окно модуля формы (в ответ
на вопрос о необходимости сохранения модуля следует выбрать No, т. е. модуль,
соответствующий закрытой форме, сохранять не надо). Затем из меню File нужно
выбрать команду New | Unit. В результате открывается окно редактора кода,
в котором находится сформированный Delphi шаблон модуля. Его текст приведен
в листинге 6.7.
Листинг
6.7. Шаблон модуля
unit
Unit1; interface implementation end.
Начинается
модуль заголовком — инструкцией unit, в которой указано имя модуля. Во время
сохранения модуля это имя будет автоматически заменено на имя, указанное программистом.
Слово interface
отмечает раздел интерфейса модуля. В этот раздел программист должен поместить
объявления находящихся в модуле процедур и функций, которые могут быть вызваны
из других модулей, использующих данный.
В раздел
implementation (реализация) нужно поместить процедуры и функции, объявленные
в разделе interface.
В качестве
примера в листинге 6.8 приведен модуль программиста, который содержит рассмотренные
ранее функции IsInt и isFioat.
Листинг
6.8. Модуль программиста
unit
my__unit;
interface
// объявления процедур и функций,
// доступных
программам,
// использующим
этот модуль function IsInt(ch : char) : Boolean; // функция Islnt проверяет, является ли символ
// допустимым
во время ввода целого числа
function
IsFloat(ch : char; st: string) : Boolean;
// Функция
IsFloat проверяет, является ли символ допустимым
// во время
ввода дробного числа
// ch — очередной
символ
// st — уже
введенные символы
implementation
// реализация // проверяет, является ли символ допустимым // во время ввода целого числа function Islnt(ch : char) : Boolean;
begin
if (ch
>= '0') and (ch <= '9') // цифры
or (ch
= #13) // клавиша <Enter>
or (ch
= #8) // клавиша <Backspace> then Islnt := True // символ допустим else Islnt := False; // недопустимый символ
end; // проверяет, является ли символ допустимым
// во время
ввода дробного числа function IsFloat(ch : char; st: string) : Boolean; // ch — очередной символ // st — уже введенные символы
begin
if (ch
>= '0') and (ch <= '9') // цифры
or (ch
= #13) // клавиша <Enter>
or (ch
= #8) // клавиша <Backspace>
then
begin IsFloat := True; // символ верный
Exit; // выход
из функции
end; case
ch of '-': if Length(st) = 0 then IsFloat := True; ',':
if (Pos(',',st)
= 0)
and (st[Length(st)]
>= '0') and (st[Length(st)] <= '9') then // разделитель можно ввести только после цифры
// и если
он еще не введен
IsFloat := True;
else // остальные символы запрещены
IsFloat := False;
end // это раздел
инициализации // он в данном случае не содержит инструкция end.
Сохраняется
модуль обычным образом, т. е. выбором из меню File команды Save. Вместе
с тем, для модулей повторно используемых процедур и функций лучше создать отдельную
папку, назвав ее, например, Units.
Для того
чтобы в программе могли применяться функции и процедуры модуля, программист
должен добавить этот модуль к проекту и указать имя модуля в списке используемых
модулей (обычно имя модуля программиста помещают в конец сформированного Delphi
списка используемых модулей).
В листинге
6.9 приведен вариант программы Поездка на дачу. Процедура обработки события
onKeyPress в полях ввода исходных данных обращается к функции IsFloat, которая
находится в модуле my_unit.pas, поэтому в списке используемых модулей указано
имя модуля my_unit.
Листинг
6.9. Использование функции из модуля программиста unit fazenda_;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls,
my_unit; // модуль программиста
type
TForm1 = class(TForm)
Edit1: TEdit;
// расстояние
Edit2: TEdit;
// цена литра бензина
Edit3: TEdit;
// потребление бензина на 100 км
CheckBoxl: TCheckBox;
// True — поездка туда и обратно
Button1: TButton;
// кнопка Вычислить
Label4: TLabel;
// поле вывода результата расчета
Label1: TLabel;
Label2: TLabel;
Label3: TLabel; procedure EditlKeyPress(Sender: TObject;
var Key:
Char); procedure Edit2KeyPress(Sender: TObject;
var Key:
Char); procedure Edit3KeyPress(Sender: TObject;
var Key:
Char); procedure Button1Click(Sender: TObject);
private
{ Private
declarations} public
{ Public
declarations } end;
var
Form1: TForm1;
implementation
{$R *.dfm}
// нажатие
клавиши в поле Расстояние procedure TForml.EditlKeyPress(Sender: TObject;
var Key:
Char);
begin
if Key = Char(VK_RETURN) then Edit2.SetFocus // переместить курсор в поле Цена else If not IsFloat(Key,Edit2.Text)
then Key
:= Chr(O);
end;
// нажатие
клавиши в поле Цена
procedure
TForm1.Edit2KeyPress(Sender: TObject; var Key: Char);
begin
if Key = Char(VK_RETURN) then Edit3.SetFocus // переместить курсор в поле Потребление . else If not IsFloat(Key,Edit2.Text) then Key := Chr(0);
end;
// нажатие
клавиши в поле Потребление procedure TForm1.EditSKeyPress(Sender: TObject;
var Key:
Char);
begin
if Key
= Char(VK_RETURN) then Button1.SetFocus // // сделать активной кнопку Вычислить
else If not
IsFloat(Key,Edit2.Text) then Key := Chr(0);
end;
// щелчок
на кнопке Вычислить
procedure
TForml.ButtonlClick(Sender: TObject);
var
rast : real;
// расстояние
cena : real;
// цена
potr : real;
// потребление на 100 км
summ : real;
// сумма mes: string;
begin
rast := StrToFloat(Editl.Text)
;
cena := StrToFloat(Edit2.Text);
potr := StrToFloat(Edit3.Text);
summ := rast
/ 100 * potr * cena;
if CheckBoxl.Checked
then summ := summ * 2;
mes := 'Поездка
на дачу';
if CheckBox1.Checked
then
mes : = mes
+ ' и обратно' ; mes := mes + 'обойдется в '
+ FloatToStrF(summ,ffGeneral,
4,2) + ' руб.'; Label4.Caption := mes;
end; end.
После добавления
имени модуля в список модулей, используемых приложением, сам модуль нужно добавить
в проект. Для этого из меню Project надо выбрать команду Add to Project
и в открывшемся диалоговом окне — имя файла модуля. В результате добавления
модуля к проекту в окне редактора появится вкладка с текстом добавленного к
проекту модуля.
Увидеть структуру
проекта можно в окне Project Manager, которое появляется в результате
выбора соответствующей команды из меню View. В качестве примера на рис.
6.3 приведена структура проекта Поездка на дачу.
Рис.
6.3. Структура проекта отражается в окне Project Manager
После добавления
модуля к проекту и включения его имени в список используемых модулей (инструкция
uses) можно выполнить компиляцию программы.
|
®Сайт разработал: Nek по вопросам пишите сюда NekSuper@yandex.ru |