Рекурсия
End.
Begin
Begin
Begin
Begin
Begin
End.
Begin
Begin
End.
Begin
Begin
End.
Begin
Begin
Begin
End.
Begin
Begin
Begin
End.
Begin
p := (x + y + z)/2.0;
Geron := Sqrt(p*(p – x)*(p – y)*(p – z));
End; конец функции
Begin начало основной (головной) программы
ClrScr;
a := 3.0; инициализация фактических параметров
b := 4.0;
c := 5.0;
s := Geron(a, b, c); обращение к функции с фактическими параметрами a, b, c
WriteLn(‘Площадь треугольника равна ’, s:6:2);
ReadLn;
Так как функция вычисляет единственное значение типа, указанного в заголовке функции и присваиваемое имени самой функции, то ее можно использовать в любых выражениях, корректных для данного типа (в правых частях операторов присваивания).
Таким образом, формальные параметры описываются в заголовке функции и используются для реализации заданного алгоритма. Фактические же параметры – это конкретные имена или значения переменных (структур), для которых производятся вычисления по данному алгоритму; они заменяют собой формальные параметры в момент обращения к функции. То есть, формальные параметры определяют, как вычислять (алгоритм), а фактические – для чего вычислять.
Структура функции, как и структура всех программ Паскаля, включает следующие разделы:
описание меток Label,
определение констант Const
определения типов Type
описание переменных Var
описание процедур и функций Function, Procedure
операторы функции Begin … End;
Построение и использование функций подчиняется следующим правилам:
1.имя функции не должно совпадать со служебными словами, именами стандартных функций, именем основной (головной) программы или именами переменных в ней,
2. в программе не должно быть двух функций с одинаковыми именами,
3. формальные и фактические параметры должны совпадать по порядку следования, количеству и типам,
4.типы функции и переменной, которой присваивается ее значение в вызывающей программе, должны совпадать,
5. формальные параметры не должны совпадать по имени:
Function Err(x, y: Word; a, b, y: Real): Real; ошибка!
6. в конце функции обязательно должен присутствовать операторприсваивания, в левой части которого стоит имя этой функции без списка формальных параметров; в противном случае функция ничего не вычислит:
Function Summa(x, y: Real): Real;
Var s : Real;
s := x + y; эта функция ничего не вычисляет!
End;
7. помимо формальных параметров, в функции могут использоваться локальные переменные. Они описываются внутри функции в разделе Var , существуют только в ней и служат для реализации алгоритма вычислений. При выходе из функции их значения исчезают:
Function Fact(n: Word): Word; n – формальный параметр
Var i, f : Word; i, f – локальные переменные
Begin эта функция вычисляет факториал
f:=1; заданного числа
For i:=1 To n Do
f := f * i;
Fact := f;
End;
8. если в функции используется цикл с параметром (цикл For), то параметр цикла должен быть описан внутри этой функции, то есть он должен быть обязательно локальной переменной (предыдущий пример),
9. связь вызывающей (головной) программы с функцией может осуществляться как через формальные и фактические, так и через глобальные параметры. Глобальные параметры (переменные) не описываются ни в заголовке функции, ни внутри ее. Они описываются в вызывающей программе и существуют как в ней, так и в любой функции, вызываемой из этой программы. Глобальные переменные могут изменять свои значения как в вызывающей программе, так и внутри функции:
Program Global;
Uses CRT;
Var a, b, c, s : Integer; с - глобальная переменная
Function Sum(x, y : Integer) : Integer;
c := c + 1; изменение значения глобальной
Sum := x + y + c; переменной в функции
End;
a := 1;
b := 1;
c := 1; инициализация глобальной переменной
s := c + Sum(a, b);
WriteLn(‘s=’, s);
ReadLn;
После выполнения этой программы s = 5.
Следует избегать использования глобальных переменных в функциях, так как это может привести к непредсказуемым результатам вычислений. В предыдущем примере значение переменной s определялось оператором присваивания:
s := c + Sum(a, b);
и принимало значение s = 5. Поменяем местами слагаемые в этом операторе присваивания:
s := Sum(a, b) + c;
Казалось бы, результат измениться не должен, но после вычислений получим
s = 6 - значение глобальной переменной c изменилось внутри функции и стало равным с = 2, и с таким значением оно было добавлено в общую сумму в вызывающей программе.
Локальные и глобальные переменные размещаются в оперативной памяти в различных сегментах (частях). При трансляции программы формируются:
· сегмент кода, в котором хранится программа в виде машинных команд,
· сегмент данных, в котором выделяется память под глобальные переменные,
· сегмент стека, предназначенный для размещения локальных переменных:
Размер каждого сегмента не может превышать 64Кбайт. Адреса сегментов хранятся во время выполнения программы в сегментных регистрах:
CS – адрес сегмента кода,
DS – адрес сегмента данных,
SS – адрес сегмента стека.
В связи с размещением в различных сегментах памяти, глобальные и локальные переменные действуют по-разному:
· время жизни глобальных переменных – от начала работы программы до ее завершения,
· если глобальные переменные не были инициализированы явным образом, то есть им не были присвоены начальные значения, то перед началом работы программы они обнуляются,
· время жизни локальных переменных – с момента вызова подпрограммы и до ее окончания,
· значения локальных переменных между двумя вызовами одной и той же подпрограммы не сохраняются,
· неинициализированные локальные переменные предварительно не обнуляются, а принимают произвольные значения, зависящие от информации, оставшейся в занимаемых ими ячейках памяти,
· если переменная внутри подпрограммы определена в разделе описания константConst , то память под нее выделяется не в сегменте стека, а в сегменте данных, причем начальное значение ей присваивается один раз до начала работы программы, а не при входе в подпрограмму:
Function Func(k: Word) : Word;
Const n: Word = 0; n – типизированная константа
Var i: Word;
WriteLn(‘i=’,i); неопределенное значение переменнойi
WriteLn(‘n=’,n);
n:= n + k;
Func:=n;
End;
Переменной i будет присвоено заранее неизвестное значение. При первом обращении к функции переменная n, определенная в CONST, будет равна нулю, и при каждом последующем обращении она будет увеличиваться на k.
· время жизни такой переменной – время работы всей программы: значения этой переменной сохраняются между вызовами подпрограммы,
· область действия такой переменной – подпрограмма, в которой она описана, то есть вне подпрограммы к этой переменной обратиться нельзя,
10. локальные и глобальные переменные могут совпадать по имени; в этом случае в функции работают локальные переменные,
11. в качестве формальных параметров функций можно использовать имена переменных любого типа, имена массивов, множеств, файлов, записей, комбинированных структур, а также имена ранее определенных функций;
в качестве формальных параметров функций нельзя использовать конкретные значения (числа, символы), элементы массивов, поля записей, выражения и стандартные функции,
12. в качестве фактических параметров функций можно использовать константы, переменные, имена и элементы массивов, множеств, файлов, имена и поля записей, комбинированных структур, а также выражения, стандартные функции и имена ранее описанных функций,
13. значения входных (фактических) параметров функций не изменяются, даже если соответствующие им формальные параметры изменяются внутри функции; такие не изменяющиеся функцией входные переменные называются параметрами-значениями. При использовании параметров-значений в функцию передаются не сами фактические параметры, а их копии. Поэтому сами параметры остаются всегда неизменными:
Program Primer;
Uses CRT;
Var a, b, c : Integer;
Function Sum(x, y : Integer) : Integer; x, y – параметры-значения
x := x + 1; изменение значений формальных
y := y + 1; параметров в функции
Sum := x + y;
End;
a := 1;
b := 1;
c := Sum(a, b);
WriteLn(‘a=’, a, ‘ b=’, b);
ReadLn;
Входные значения фактических параметров x = 1, y = 1. После выполнения программы они останутся теми же, хотя внутри функции соответствующие им формальные параметры изменились,
14. в качестве входных переменных можно использовать параметры переменные; их значения могут изменяться функцией, и эти изменения сохраняются при выходе из функции. Они описываются в списке формальных параметров функции с добавлением слова Var:
Program Primer;
Uses CRT;
Var a, b, c : Integer;
Function Sum(Var x, y : Integer) : Integer; x, y – параметры-переменные
x := x + 1; изменение значений формальных
y := y + 1; параметров в функции
Sum := x + y;
End;
a := 1;
b := 1;
c := Sum(a, b);
WriteLn(‘a=’, a, ‘ b=’, b);
ReadLn;
Входные значения фактических параметров x = 1, y = 1. После выполнения программы они изменятся и примут значения x = 2, y = 2.
При использовании параметров-переменных в функцию передаются не копии фактических параметров, как это имело место с параметрами-значениями, а адреса фактических параметров, что позволяет сохранять их измененные функцией значения.
Зачастую использование параметров-переменных может тоже привести к непредсказуемым результатам вычислений:
Program Primer;
Uses CRT;
Var a, b : Integer;
Function Nemo(Var x : Integer; y : Integer) : Integer;
x – параметр-переменная,
y – параметр-значение
x := x + y;
a := y;
Nemo := x;
End;
a := 1;
b := 2;
WriteLn(‘Nemo=’, Nemo(a, b),‘ a=’, a, ‘ b=’, b);
ReadLn;
Результат работы программы:
Nemo=2 a=2 b=2
Значение переменной a будет испорчено, так как значения переменных a и x будут записываться в одной ячейке памяти, а функция Nemo вместо 3 примет значение, равное 2. Ошибки такого рада трудно найти, поэтому в функциях и не рекомендуется использовать параметры-переменные,
15. в заголовке функции нельзя использовать формальные параметры безымянных типов (стандартные типы считаются поименованными):
Function Func(x : 1..10; r : array [1..20] Of Real) : Real;
Типы формальных параметров x и r являются безымянными, так как не относятся к стандартным. Поэтому в вызывающей эту функцию программе необходимо определить новые типы данных:
Type TCount = 20;
TVector = Array [1 .. TCount] Of Real;
TInterval = 1 .. 10;
а в заголовке функции использовать эти новые типы:
Function Func(x : TInterval; r : TVector) : Real;
16. в любой функции в качестве формальных параметров могут быть использованы другие функции, составленные программистом – параметры-функции.
Требования к таким функциям:
ü их тип должен быть определен в разделе описания типов Type,
ü они не должны быть стандартными,
ü они не должны быть вложенными,
ü они должны иметь только параметры-значения,
ü они должны быть откомпилированы с использованием директивы компилятору {$F+} – использование дальнего типа вызова подпрограмм.
Пример: создать функцию для определения суммы, произведения двух чисел и произведения их суммы на их разность. Функции для выполнения этих операций описать как параметры-функции:
Program Param_func;
Uses CRT;
Type TFunс = Function (x, y: Integer): Integer; описан процедурный тип TFunc – целой функции двух аргументов целого типа
Var a, b, c: Integer;
{$F+} директива компилятору- использование дальнего типа вызова подпрограмм
Function Add(x, y: Integer): Integer; функция для сложения двух переменных
Add := x + y;
End;
Function Mult(x, y: Integer): Integer; функция для перемножения двух переменных
Mult := x * y;
End;
Function Funny(x, y: Integer): Integer;
Funny := (x + y) * (x - y);
End;
{$F-} отмена директивы
функция, использующая параметр-функцию operation
Function Par_func(m, n : Integer; operation : TFunc): Integer;
Par_func := operation(m, n);
End;
ClrScr;
a := 5;
b := 3;
c := Par_func(a, b, Add); фактические параметры для параметров-функций не указываются!
WriteLn('c=', c);
c := Par_func(a, b, Mult);
WriteLn('c=', c);
c := Par_func(a, b, Funny);
WriteLn('c=', c);
ReadLn;
На экран будет выведено:
с=8
с=15
с=16
17. в Паскале разрешено использовать рекурсию – обращение функции самой к себе, при этом имя функции со списком формальных параметров будет стоять в операторах функции справа от знака присваивания.
Использование рекурсии в программировании базируется на рекурсивных математических определениях. Считается, что в математике рекурсивность как принцип определений используется с 1890 года. Впервые применил ее Д.Гильберт.
Основная идея рекурсии – определить некий объект через самого себя, по крайней мере, частично. Оказалось, что с помощью рекурсии удобно описывать различного рода последовательности, подчиняющиеся определенным закономерностям.
Например, вычисление факториала целого неотрицательного числа n! = 1·2·3·…·(n-1) · n . Кроме того, по определению, 0! = 1. Рекурсивное математическое определение факториала имеет вид:
1 при n = 0,
n!=
(n – 1)!·n при n > 0.
Последовательность чисел Фибоначчи имеет вид 1, 1, 2, 3, 5, 8, 13…
В ней два первых числа фиксированы и равны единице, а каждое последующее число равно сумме двух предыдущих. Рекурсивное математическое определение числа Фибоначчи с порядковым номером n имеет вид:
1 при n = 1,
Fn= 1 при n = 2,
Fn-2 + Fn-1 при n > 2.
Общей особенностью этих рекурсивных определений является то, что некий сложный объект определяется через себя же (рекурсивно обращается к себе же), но в более простом исполнении. Рекурсивные математические определения отличаются особой лаконичностью, что и подтверждается вышеприведенными примерами.
На базе рекурсивных определений можно построить компактные и выразительные подпрограммы. Вполне очевидно, что за любым из приведенных рекурсивных определений прячется некий циклический процесс вычислений. Такой циклический процесс допускает реализацию на базе некоей рекуррентной формулы, производной от соответствующего рекурсивного определения. Рекуррентные формулы являются составными и определяют числовые последовательности, в которых каждый очередной член зависит от одного или нескольких предыдущих. При этом для рекуррентной формулы характерно, что она представляет собой зависимость очередного члена последовательности от строго определенных предыдущих ее членов. Составной частью рекуррентной формулы является прямое определение одного или нескольких начальных членов последовательности. Чаще всего определяемая последовательность бесконечна, поэтому требуется указать требуемое количество ее членов. Трансформируем вышеприведенные рекурсивные математические определения в рекуррентные формулы.
Рассмотрим последовательность факториалов целых чисел 0!, 1!, 2!, 3!,…, в которой ai = i!, i = 1, 2, 3,… Эту же последовательность можно представить в виде рекуррентной формулы: ai = ai-1·i, a0 = 1, i = 1, 2, 3… Эта формула задает последовательность, в которой каждый очередной член зависит непосредственно от предшествующего. Начальный член последовательности a0 задан прямою. Найдя член последовательности с порядковым номером i = n, мы тем самым решим задачу вычисления n!
Рекуррентная формула для вычисления числа Фибоначчи с заданным порядковым номером i = n практически не отличается от рекурсивного определения: Fi = Fi-2 + Fi-1 , F1 = 1 , F2 = 1 , i = 3, 4, 5,…
Итак, функция считается рекурсивной, если при решении задачи она обращается к самой себе или непосредственно, или через другие подпрограммы. Известно, как технически реализуется обращение к подпрограмме в общем случае:
1. запоминается состояние программы, вызывающей подпрограмму – адрес точки, следующей за оператором обращения к подпрограмме, чтобы знать, куда вернуться после ее выполнения,
2. в свободном месте памяти располагаются все необходимые локальные переменные вызываемой подпрограммы, а также копии тех ее параметров, которые передаются по значению,
3. выполняются операторы подпрограммы, затем освобождается временно занятая область памяти, после чего осуществляется возврат в нужную точку программы, вызвавшей подпрограмму.
При рекурсивном обращении каждый раз приходится запоминать не только адрес возврата, но и всю совокупность данных вызывающей подпрограммы (локальные переменные и параметры-значения). С этой целью используется автоматически выделяемая область памяти – стек, структура, работающая по принципу LIFO (Last in – first out: последним пришел – первым вышел). Такой метод работы с памятью обеспечивает строгое соответствие прямого порядка записи данных обратному порядку их чтения. Только с помощью стека можно достаточно просто обеспечить корректное завершение работы цепочки подпрограмм, каждая из которых вызывает следующую: сначала должна быть завершена последняя, затем – предпоследняя и так далее. Максимальный размер стека – 65520 байт. Поэтому последовательность рекурсивных обращений не может быть бесконечной. В любой рекурсивной подпрограмме должна быть нерекурсивная (терминальная) ветвь, обеспечивающая выход из рекурсии. При переполнении стека работа программы прерывается, и появляется сообщение об ошибке Error 202: Stack overflow error.
Рекурсивная функция, вычисляющая факториал заданного числа n, может иметь вид:
Function Factorial(n: Word): Word;