операторы Delphi. Операторы delphi оператор присваивания
Скачать 92.84 Kb.
|
ПРОЦЕДУРЫ И ФУНКЦИИЕсли необходимо многократно проводить какие–то вычисления для разных значений аргументов, то такие вычисления оформляются в виде подпрограммы «функции», если результатом вычислений является одна величина, в других случаях в виде подпрограммы – «процедуры». В общем виде подпрограмма «процедура» определяется следующим образом: Procedure Имя процедуры(Список формальных параметров); директивы; Локальные описания begin Операторы процедуры end; Вызов процедуры осуществляется указанием имени процедуры со списком фактических параметров. Формальные параметры в списке разделяются точкой с запятой. Для работы с подпрограммами используется специальная область памяти стек, который работает по принципу: первым вошел – последним вышел. Стек используется при вызове подпрограмм. В него записываются адрес возврата в вызывающую процедуру после окончания работы подпрограммы и передаваемые в подпрограмму параметры. В стеке отводится также место для всех внутренних переменных процедуры. При вызове подпрограммы происходит заполнение стека, а при выходе из процедуры из него исключается область памяти, которая была выделена процедуре при ее вызове. Одна процедура может вызвать другую, та – следующую и т.д., при этом происходит заполнение стека, а при выходе из каждой процедуры – его освобождение. Существует пять видов формальных параметров: Параметры–значения. Перед такими параметрами не ставится никаких ключевых слов. Значения таких параметров передаются через стек и в вызывающую программу они не возвращаются. Такими параметрами не могут быть файловые переменные и структуры, их содержащие. Параметры–переменные. Перед такими параметрами записывается ключевое слово Var. В этом случае через стек в подпрограмму передается адрес фактического параметра, и поэтому изменение этого параметра в подпрограмме приводит к его изменению и для вызывающей программы. Параметры–константы. Перед такими параметрами записывается ключевое слово Const. Эти параметры похожи на внутренние константы или параметры, доступные только для чтения. Параметры константы подобны параметрам переменным, только внутри подпрограммы им нельзя присваивать никаких значений и их нельзя передавать в другие подпрограммы, как параметры переменные. Выходные параметры. Перед такими параметрами записывается ключевое слово Out. Эти параметры имеют такие же свойства, как и параметры переменные, однако им не присваиваются какие–либо значения при вызове подпрограммы. Нетипированные параметры. Для таких параметров не указывается тип параметра. При этом через стек передается адрес фактического параметра, а внутри подпрограммы по этому адресу можно расположить переменную любого типа. При использовании подпрограмм нужно придерживаться следующих правил: фактическими параметрами при вызове подпрограмм могут быть переменные, константы и целые выражения; формальными параметрами при описании подпрограмм могут быть только имена переменных; фактические и формальные параметры должны быть согласованы по типу, порядку следования и количеству. Для принудительного выхода из подпрограммы используется оператор Return. Подпрограмма «функция» определяется следующим образом: Function Имя функции(Список формальных параметров): тип результата; директивы; Локальные описания begin Операторы функции end; Вызов подпрограммы «функции» осуществляется указанием в правой части оператора присваивания имени функции со списком фактических параметров. Внутри функции обязательно нужно присвоить какое–то значение имени функции. Кроме имени функции можно применять ключевое слово Result, которое можно использовать в любом выражении, в то время как использование имени функции в выражении приводит к повторному вызову этой же функции, т.е. рекурсии. Подпрограммы могут иметь директивы, которые записывают после объявления подпрограммы. Эти директивы могут определять правила вызова подпрограмм и передачи в них параметров. Директивой по умолчанию является директива Register, которая так же, как и Pascal, определяет передачу параметров в стек слева направо, т.е. так, как они записаны в определении подпрограммы. Директивы Cdecl, Stdcall и Safecall определяют передачу параметров наоборот, т.е. справа налево, как это принято в языке Си. Для всех директив, кроме Сdecl, освобождение стека происходит до выхода из подпрограммы, а для Сdecl – после передачи управления вызывающей программе. Директива Register передает первые три параметра через регистры процессора, в то время как остальные передают все параметры через стек. Директива Safecall используется при работе с OLE Automation интерфейсами. Директивы Near, Far и Export использовались только в 16–битных приложениях и в новых версиях Delphi оставлены только для совместимости со старыми программами. Директива Forward означает, что дано только описание параметров вызова процедуры, а само описание тела процедуры будет дано ниже. Такая ситуация возникает, когда, допустим, процедура А вызывает процедуру В, в то время как процедура В сама вызывает процедуру А. В этом случае можно сначала описать параметры процедуры В с директивой Forward, затем поместить процедуру А, а затем текст процедуры B. Это связано с тем, что Delphi не разрешает использовать не определенные заранее процедуры. Директива Forward может быть использована только в секции Implementation модуля Unit. Директива External используется при описании правил вызова процедуры, когда ее тело извлекается из динамической библиотеки (DLL) или из объектного модуля (OBJ) – результата трансляции с языка Си. Директива OverLoad используется тогда, когда две процедуры имеют одно имя, но разный список параметров. При вызове таких процедур предварительно проверяется список фактических параметров и вызывается та процедура, список формальных параметров которой совпадает по типу со списком фактических параметров. В локальных описаниях процедур и функций могут быть объявлены внутренние типы, константы, переменные и другие процедуры и функции. Во внутренних процедурах и функциях доступны все локальные описания подпрограммы, в которой они сами описаны. Описания, сделанные в файле проекта с расширением *.dpr или в интерфейсной части модулей Unit, являются глобальными и доступными для всех подпрограмм проекта или модулей Unit. Эти переменные и константы располагаются в специальном сегменте данных, а не в стеке. Для принудительного выхода из текущей процедуры или функции можно использовать процедуру Exit. Рассмотрим пример расчета таблицы значений следующей функции: ⎛ n n+1 ⎞ y = sin2 ⎜⎟. ⎜⎟ ⎝ i=1 j=1 ⎠ Переменная x будет изменяться в интервале a ≤ x ≤ b с шагом h=(b-a)/10. Здесь m n! n Cn = (n − m)! – число сочетаний из n по m, n!= ∏i:=1 i – число перестановок по m!⋅ n. Оформим расчет числа перестановок, сочетаний и сумм в виде подпрограмм функций. Исходные данные будем определять в компонентах TEdit, а результат вычислений поместим в многострочный редактор TMemo. Обработчик нажатия клавиши «Вычислить» будет иметь следующий вид: Procedure TForm1.Button1Click(Sender: TObject); Var n:integer; a,b,h,x,y:Extended; Function F(n:Integer):Extended; // Функция расчета факториала Var i:Integer; Begin Result:=1; // Вначале полагаем произведение равным единице For i:=1 to n do // Организуем цикл умножений Result:=Result*i; End; Function C(n,m:Integer):Extended; // Расчет числа сочетаний Begin C:=F(n)/F(m)/F(n-m); End; Function Sum(n:Integer;x:Extended):Extended; // Расчет суммы Var i:Integer; Begin Result:=0; // Сумму полагаем равной нулю For i:=1 to n do // Организуем цикл сложений Result:=Result+C(2*i,x)*Cos(i*x); End; Begin // Тело обработчика нажатия клавиши «Выполнить» N:=strtoint(Edit1.Text); // Определяем начальные значения переменных a:=strtofloat(Edit2.Text); b:=strtofloat(Edit3.Text); h:=(b-a)/10; // Расчет шага по X x:=a; // X полагаем равным начальному значению While x// Организуем цикл по X Y:=Sqr(sin(Sum(n,x)/Sum(n+1,2*x))); // Рассчитываем Y // Выводим результаты Memo1.Lines.Add(’x=’+Floattostr(x)+’ y=’+Floattostr(y)); x:=x+h; // Наращиваем X end; end; Рассмотрим еще один пример уже с использованием процедур. Необходимо написать программу по возведению квадратной матрицы в любую степень, т.е. B=Am, где A – заданная квадратная матрица размерности n:
n умножения двух матриц, т.е. C=A⋅B или cij = ∑aik ⋅bkj . k:=1 Размерность матрицы n и степень m будем задавать в компонентах TEdit. Начальную матрицу А будем определять случайным образом и помещать в компонент StringGrid1, а результат возведения в степень разместим в компоненте StringGrid2. Обработчик нажатия клавиши «Вычислить» будет иметь следующий вид: Procedure TForm1.Button1Click(Sender: TObject); Const nmax=20; //Объявление констант, типов и переменных Type Ta=Array[1..nmax,1..nmax] of extended; Var i,j:integer; A,B,C:Ta; // Процедура перемножения матриц Procedure UM(n:integer; var A,B,C:Ta); Var i,j,k:Integer; s:Extended; // Внутренние переменные процедуры
RowCount:=n; ColCount:=n; FixedRows:=0; FixedCols:=0; End; With StringGrid2 do Begin RowCount:=n; ColCount:=n; FixedRows:=0; FixedCols:=0; End; Randomize; // Инициализация датчика случайных чисел For i:=1 to n do // Заполнение матрицы А случайными числами For j:=1 to n do Begin A[i,j]:=random; B[i,j]:=A[i,j]; StringGrid1.Cells[j-1,i-1]:=FloattoStrF(A[i,j], ffGeneral, 10,5); End; For k:=1 to m-1 do Begin // Организация цикла возведения в степень А UM(n,A,B,C); For i:=1 to n do // Запоминание текущего результата умножения матриц For j:=1 to n do B[i,j]:=C[i,j]; End; For i:=1 to n do // Вывод результатов вычислений For j:=1 to n do StringGrid2.Cells[j-1,i-1]:=FloattoStrF(C[i,j], ffGeneral, 10,5); End; Процедуры и функции, которые прямо или косвенно вызывают сами себя, называют рекурсивными. Приведем пример такой функции для расчета факториала: Function F(n:Integer):Extended; Begin If n>1 then F:=F(n-1)*n else F:=1; End; Расчет большинства полиномов осуществляется по рекуррентным формулам, когда каждый последующий полином рассчитывается через предыдущие полиномы. Например, B0=1, B1=X, Bk=2⋅X⋅Bk-1-Bk-2. Для расчета таких полиномов можно тоже использовать рекурсивную функцию вида: Function B(n:Integer; x:Extended):Extended; Begin If n>1 then B:=2*X*B(n-1,x)-B(n-2,x) else If n=1 then B:=x else B:=1; End; Как видно из этих примеров, рекурсивные функции получаются очень компактными, но они требуют большой аккуратности при их написании – небольшая ошибка в программе может приводить к переполнению стека из–за бесконечного вызова функции самой себя. |