Автор работы: Пользователь скрыл имя, 16 Сентября 2009 в 13:41, Не определен
Учебник по программированию
end.
Обратите внимание: для установления правильных связей функций SIN1 и COS1 с Процедурой PRINTFUNC они должны компилироваться с расчетом на дальнюю модель памяти. Вот почему в программу вставлены стандартные директивы FAR сразу за заголовками функций.
В таком режиме должны компилироваться любые процедуры (функции), которые будут передаваться в качестве фактических параметров вызова.
Стандартные процедуры (функции) Турбо Паскаля не могут передаваться рассмотренным способом.
В программе могут быть объявлены переменные процедурных типов, например, так:
var
p1 : Proс1;
fl, f2 : Func2;
ар
: array [1..N] of Proс1;
Переменным процедурных типов допускается присваивать в качестве значений имена соответствующих подпрограмм.
После такого присваивания имя переменной становится синонимом имени подпрограммы, например:
type
Proc = Procedure (n: word; var a: Byte) ;
var
ProcVar: Proc;
x, у : Byte;
Procedure Proc1(x: word; var y: Byte); far;
begin
if x > 255 then
y:=x mod 255
else
у := Byte(x)
end;
begin {Главная.программа}
ProcVar := Proc1;
for x := 150 to 180 do
begin
ProcVar (x + 100, у);
Write (у:8)
end
end.
Разумеется, такого рода присваивания допустимы и для параметров-функций, пример:
type
FuncType = Function (i : Integer) : Integer;
vаг
VarFunc : FuncType;
i : Integer;
Function MyFunc (count : Integer) : Integer; far;
Begin
....
end; {MyFunc}
begin {Основная программа}
…..
i :=MyFunc(l); (Обычное использование результата функции}
…..
VarFunc := MyFunc; {Присваивание переменной процедурного типа имени функции MyFunc}
……
end.
Отметим, что присваивание
VarFunc
:= MyFunc(l);
будет недопустимым, так как слева и справа от знака присваивания используются несовместимые типы: слева - процедурный тип, а справа - INTEGER; имя функции со списком фактических параметров MyFunc(1) трактуется Турбо Паскалем как обращение к значению функции, в то время как имя функции без списка параметров рассматривается как имя функции.
В
отличие от стандартного Паскаля, в
Турбо Паскале разрешается
8.5. НЕТИПИЗИРОВАННЫЕ ПАРАМЕТРЫ-ПЕРЕМЕННЫЕ ,,
Еще одно очень полезное нововведение фирмы Borland - возможность использования нетипизированных параметров.
Параметр считается
Нетипизированные параметры обычно используются в случае, когда тип данных несущественен.
Такие ситуации чаще всего возникают при разного рода копированиях одной области памяти в другую, например, с помощью процедур BLOCKREAD, BLOCKWRITE, MOVE и т.п.
Нетипизированные
параметры в сочетании с
В примере 8.4 функция NORMA вычисляет норму вектора, длина которого меняется случайным образом. Стандартная константа MAXINT содержит максимальное значение целого типа INTEGER и равна 32767.
Следует учесть, что при обращении к функции NORMA массив Х помещается в стек и передается по ссылке, поэтому описание локальной переменной А в виде одномерного массива максимально возможной длины в 65532 байта (встроенная константа MAXINT определяет максимально возможное значение типа INTEGER и равна 32767), совпадающего с X, на самом деле не приведет к выделению дополнительного объема памяти под размещение этой переменной.
Иными словами, переменная А - фиктивная переменная, размер которой никак не влияет на объем используемой памяти, С таким же успехом можно было бы объявить ее в виде массива из одного элемента, правда, в этом случае необходимо позаботиться об отключении контроля выхода индекса за границы диапазона.
Пример 8.4
const
NN = 100; {Максимальная длина вектора}
var
а : array [1..NN] of Real;
i, j, N : Integer;
{------------------}
Function Norma (var x; N: Integer): Real;
var
a : array [1..2*MaxInt div SizeOf(Real)] of Real absolute x;
i : Integer;
s : Real;
begin {Norma}
s :=0;
for i :=1 to N do
s := s + sqr(a[i]) ;
Norma := sqrt(s)
end {Norma};
{------------------}
begin {main}
for i := 1 to 10 do
begin
N := Random(NN) + 1; {Текущая длина вектора}
for j := 1 to N do
a[j] := Random;
WriteLn ('N = ', N:2, ' норма =', Norma (a, N) : 10:7)
end
end {main}.
Как видно из рассмотренного примера, передача одномерных массивов переменной длины не вызывает никаких трудностей.
Сложнее обстоит дело с многомерными массивами, однако и в этом случае использование описанного приема (нетипизированный параметр и совмещение его в памяти с фиктивной переменной) все-таки проще, чем описанная в гл. 6 индексная арифметика.
Еще
раз напомню, что в случае многомерных
массивов их элементы располагаются в
памяти так, что при переходе
от младших адресов к старшим наиболее
быстро меняется самый правый индекс массива.
8.6. РЕКУРСИЯ И ОПЕРЕЖАЮЩЕЕ ОПИСАНИЕ
Рекурсия - это такой способ организации вычислительного процесса при котором подпрограмма в ходе выполнения составляющих ее операторов обращается сама к себе.
Рассмотрим классический пример - вычисление факториала (пример 18)). Программа вводит с клавиатуры целое число N и выводит на экран значение –N!, которое вычисляется с помощью рекурсивной функции FAC.
Для выхода из программы необходимо либо ввести достаточно большое целое число, чтобы вызвать nepеполнение при умножении чисел с плавающей запятой, либо нажать Ctrl-Z и Enter.
При выполнении правильно организованной рекурсивной подпрограммы осуществляется многократный переход от некоторого текущего уровня организации алгоритма к нижнему уровню последовательно до тех пор, пока, наконец, не будет получено тривиальное решение поставленной задачи.
В примере 8.5 решение при N =0 тривиально и используется для остановки рекурсии.
Пример 8.5
Program Factorial;
{$S+} {Включаем контроль переполнения стека}
var
n: Integer;
Function Fac(n: Integer): Real;
{Рекурсивная функция, вычисляющая n!}
begin {Fac}
if n < 0 then
WriteLn ('Ошибка в задании N')
else
if n=0 then
Fac := 1
else Fac := n * Fac(n-l)
end {Fac};
{-----------------}
begin {main}
repeat
ReadLn(n) ;
WriteLn('n! = ',Fac(n))
until EOF
end
{main}.
Рекурсивная форма организации алгоритма обычно выглядит изящнее итерационной и дает более компактный текст программы, но при выполнении, как правило, медленнее и может вызвать переполнение стека (при каждом входе в подпрограмму ее локальные переменные размещаются в особым образом организованной области памяти, называемой программным стеком).
Переполнение стека особенно ощутимо сказывается при работе с сопроцессором: если программа использует арифметический сопроцессор, результат любой вещественной функции возвращается через аппаратный стек сопроцессора, рассчитанный всего на 8 уровней.
Если, например, попытаться заменить тип REAL функции FAC (см. пример 8.5) на EXTENDED, программа перестанет работать уже при N = 8.
Чтобы избежать переполнения стека сопроцессора, следует размещать промежуточные результаты во вспомогательной переменной.
Вот
правильный вариант примера 8.5 для
работы с типом EXTENDED:
Program Factorial;
{$S+,N+,E+} {Включаем контроль стека и работу сопроцессора}
var
n: Integer;
Function Fac(n: Integer): extended;
var
F: extended; {Буферная переменная для разгрузки стека сопроцессора}
{Рекурсивная функция, вычисляющая п!}
begin {Fac}
if n < 0 then
WriteLn ('Ошибка в задании N')
else
if n = 0 then
Fac := 1
Else
Begin
F := Fac(n-l);
Fac := F * n
End
end {Fac};
{------------------}
begin {main}
repeat
ReadLn(n) ;
WriteLn('n! = ',Fac(n))
until EOF
end {main}.
Рекурсивный вызов может быть косвенным. В этом случае подпрограмма обращается к себе опосредованно, путем вызова другой подпрограммы, в которой содержится обращение к первой, например:
Procedure A (i : Byte) ;
Begin
……
B(i);
Procedure В (j : Byte) ;