ПРОЦЕДУРНЫЕ ТИПЫ
Основное назначение процедурных типов - дать программисту гибкие средства передачи функций и процедур в качестве фактических параметров обращения к другим процедурам и функциям. Для объявления процедурного типа используется заголовок процедуры (функции), в котором опускается ее имя, например: Type Proc1 = Procedure (a, b, с: Real; var d: Real); Proc2 = Procedure (var a, b); РгосЗ = Procedure; Func1 = Function: String; Func2 = Function (var s: String): Real; Как видно из приведенных примеров, существует два процедурных типа: тип-процедура и тип-функция. В следующий программе иллюстрируется механизм передачи процедур в качестве фактических параметров вызова. Программа выводит на экран таблицу двух функций: sin1 (х) = (sin(x) + 1) * Ехр(-х) и cosi(x) = (Cos(x) + 1) * Ехр(-х). Вычисление и печать значений этих функций реализуются в процедуре printFunc, которой в качестве параметров передается количество np вычислений функции в диапазоне х от 0 до 2*3.141592 и имя нужной функции. Function Sinl(X: Real): Real; Begin Result:= (Sin(X) + 1) * Exp(-X) end; // Sin 1 Function Cosl(X: Real): Real; Begin Result:= (Cos(X) + 1) * Exp(-X) end; // Cosi procedure TfmExample.bbRunClick(Sender: TObject); Type Func = function (X: Real): Real; // Процедурный тип Procedure PrintFunc(NP: Integer; F; Func); Var k: Integer; X: Real; Begin for k:= 0 to NP do Begin X:=k*2*pi/ NP; mmOutput.Lines.Add(FloatToStrF(X, ffExponent, 10, 2) + #9#9 + FloatToStrF(F(X), ffExponent, 10, 2)); End; end; // PrintFunc begin // bbRunClick nmiOutput.Lines.Add(#9'Функция SINI:'); PrintFunc (10, Sini); mmOutput.Lines.Add(#9'Функция COSI:'); PrintFunc (10, Cosi); End; Обратите внимание: передаваемые подпрограммы не могут быть локальными, т. е. процедурами или функциями, объявленными внутри другой подпрограммы. Вот почему описание подпрогра^.' sini и cosi размещаются вне обработчика bbRunciick, но выше не." по тексту модуля. Замечу, что символ #9 - это символ табуляции. который вставляется в формируемые строки для разделения колонок с цифрами. В программе могут быть объявлены переменные процедурных типов, например,так: Var p1: Proc1; fl, f2: Func2; ар: array [1..N] of Proc1; Переменным процедурных типов допускается присваивать в качестве значений имена соответствующих подпрограмм. После такого присваивания имя переменной становится синонимом имени подпрограммы. РЕКУРСИЯ И ОПЕРЕЖАЮЩЕЕ ОПИСАНИЕ Рекурсия - это такой способ организации вычислительного процесса, при котором подпрограмма в ходе выполнения составляющих ее операторов обращается сама к себе. Рассмотрим классический пример - вычисление факториала. Программа получает от компонента edinput целое число n и выводит в компонент lboutput значение N!, которое вычисляется с помощью рекурсивной функции Factorial. При выполнении правильно организованной рекурсивной подпрограммы осуществляется многократный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не будет получено тривиальное решение поставленной задачи. В нашем случае решение при n = 0 тривиально и используется для остановки рекурсии. procedure TfmExample.bbRunClick(Sender: TObject); Function Factorial(N: Word): Extended; begin if N = 0 then Result: = 1 else Result:= N * Factorial(N-1) End; Var N: Integer; Begin Try N:= StrToInt(Trim(edinput.Text)); Except Exit; end; IbOutput.Caption:= FloatToStr(Factorial(N)) End; Рекурсивная форма организации алгоритма обычно выглядит изящнее итерационной и дает более компактный текст программы, но при выполнении, как правило, медленнее и может вызвать переполнение стека (при каждом входе в подпрограмму ее локальные переменные размещаются в организованной особым образом области памяти, называемой программным стеком). Рекурсивный вызов может быть косвенным. В этом случае подпрограмма обращается к себе опосредованно, путем вызова другой подпрограммы, в которой содержится обращение к первой, например: Procedure A (i: Byte); Begin В (i); End; Procedure В (j: Byte); Begin а(j); End; Если строго следовать правилу, согласно которому каждый идентификатор перед употреблением должен быть описан, то такую программную конструкцию использовать нельзя. Чтобы такого рода вызовы стали возможны, вводится опережающее описание: Procedure В (j: Byte); Forward; Procedure A (i: Byte); Begin В (i); End;
|