Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Всё_о_Паскале.doc
Скачиваний:
7
Добавлен:
20.11.2018
Размер:
4.54 Mб
Скачать

18.2 Функции

О параметрах-переменных часто говорят, что подпрограмма возвращает их значения, подчеркивая тем самым, что они являются (или могут являться) выходными данными некоторого вычислительного процесса.

Распространены подпрограммы, требующие возврата всего одного выходного параметра, являющегося скаляром (то есть, единственным значением, а не вектором или матрицей). В этих случаях вместо подпрограммы-процедуры используется подпрограмма-функция, возвращающая скалярный результат своей работы в основную программу. Поэтому для функции в ее заголовке необходимо указать тип возвращаемого результата, а в теле функции должен присутствовать хотя бы один оператор присваивания, в левой части которого записывается имя функции:

Function Имя (Список формальных параметров):ТипРезультата;

{выше - заголовок подпрограммы}

Var описания локальных переменных;

Begin

{Тело функции}

Имя:=Выражение1;

End;

Здесь Выражение1 должно иметь тот же тип, что и указанный в заголовке ТипРезультата, а оператор Имя:=Выражение1; не обязан быть последним в теле функции. Поскольку результат выполнения функции возвращается в основную программу через ее имя, то обращение к функции записывается аналогично стандартным функциям в виде операнда выражения, стоящего справа от знака присваивания:

Результат:=Выражение2;

Выражение2 может состоять только из вызова функции вида Имя(список фактических параметров) или включать этот вызов как часть более сложного выражения., переменная Результат должна соответствовать типу функции. В записи выражения должны быть соблюдены все изученные ранее правила соответствия типов.

Все, сказанное выше о согласовании формальных и фактических параметров, а также о параметрах-значениях и переменных, в полной мере относится и к функциям.

Фактически, стандартные подпрограммы Паскаля также делятся на функции и процедуры, в зависимости от способа их вызова. Так, writeln относится к стандартным процедурам, а sin – к функциям. Правда, некоторые из стандартных подпрограмм имеют переменное число параметров, что запрещено пользовательским подпрограммам.

Разумеется, при использовании функций никто не запрещает возвращать более одного значения через параметры-переменные, подобные тем, что мы изучили для процедур. Можно сказать, что функции в Паскале – это процедуры, способные возвращать дополнительное скалярное значение. Во многих других языках программирования специального разделения на процедуры и функции нет.

Чтобы окончательно уяснить не-синтаксические различия между функциями и процедурами, обобщим их в таблице:

Процедура

Функция

Вызывается отдельным оператором

Вызывается из выражения справа от знака присваивания

Использует параметры-значения и переменные

Использует параметры-значения и переменные, дополнительно доступен параметр-переменная с именем, совпадающим с именем функции

Напишем и вызовем простейшую функцию, находящую максимальный из двух своих вещественных аргументов:

Function Max (a,b:real):real;

Begin

If a>b them Max:=a

Else Max:=b;

End;

Вызвать эту функцию мы могли бы так:

Var x,y,z,r,t:real;

. . .

Read (x,y,z);

r:=max(x,y); {Максимальное из значений x,y записали в r}

t:=max(max(x,y),z); {Максимальное из значений x,y,z записали в t}

Последний вызов иллюстрирует, что, как и стандартные функции, функции, написанные программистом, могут вызываться из сколь угодно сложных выражений – при условии, что мы будем соблюдать правила соответствия параметров. Написанную нами функцию max нетрудно было бы реализовать и в виде процедуры:

Procedure Max (a,b:real; var c:real);

Begin

If a>b them c:=a

Else c:=b;

End;

Однако, при вызове этой процедуры постоянно пришлось бы передавать "лишний" параметр c, служащий только для хранения возвращаемого значения.

Мы уже знакомы с оператором Halt;, позволяющим аварийно (или просто из бесконечного цикла) завершить программу. Немедленно завершить текущий блок, в том числе и подпрограмму-функцию или процедуру, позволяет оператор Exit;.

Теперь примеры.

Пр. Используя подпрограмму, вычислить сумму первых k членов ряда 1+1/n.

Сумма ряда – это скаляр, естественным выглядит использование подпрограммы-функции. Применив известные алгоритмы, составим программу:

function sum (k:integer):real;

var i:integer;

s:real;

begin

s:=0;

for i:=1 to k do s:=s+1/i;

sum:=s;

end;

var k:integer;

s:real;

begin

write ('Введите число шагов:');

readln (k);

s:=sum(k);

writeln ('Сумма=',s:8:3);

end.

Обратите внимание — несмотря на то, что функция Sum вычисляет единственную величину s, мы были бы не вправе написать в ней оператор for i:=1 to k do sum:=sum+1/i; - ведь sum – это имя функции, и справа от знака присваивания оно было бы воспринято как попытка функции sum вызвать саму себя, причем, без соблюдения правил соответствия параметров. Поэтому нам понадобилась "промежуточная" переменная s.

Тем не менее, рекурсивные функции, вызывающие сами себя, существуют и будут кратко рассмотрены чуть ниже.

Пр. Вычислить значение выражения , где z(x)= sin 2x + ln |x|.

Очевидно, что повторное вычисление выражения z(x) с различными значениями аргумента x неэффективно, куда удобнее написать подпрограмму-функцию, считающую по формуле z(x)= sin 2x + ln |x| :

function z(x:real):real;

begin

z:=sin(2*x)+ln(abs(x));

end;

begin

write ('Ответ=', (z(3)+2*sqr(z(5)))/(z(0.1)+1):6:2);

readln;

end.

Как видно из примера, использование функции позволило выполнить расчет единственным оператором (с учетом того, что writeln "умеет" печатать значения вычисленных выражений).

В заключение раздела скажем несколько слов о рекурсии. Рекурсивными называют функции, способные повторно вызывать сами себя. С точки зрения программирования, в этом нет ничего удивительного – просто при повторном входе в функцию в программном стеке создается ее новая копия и расчет выполняется заново с измененным значением параметра функции. Затем функция проверяет значение параметра, при необходимости изменяет его и вновь повторно вызывает сама себя. Разумеется, во избежание зацикливания, при некотором значении параметра должно быть предусмотрено завершение вычислительного процесса. Использование рекурсии целесообразно везде, где расчет следующего значения некоторой функции зависит от ее предыдущего значения. Так, классический пример на рекурсию – расчет факториала (факториал целого положительного числа n, обозначаемый n!, равен произведению всех чисел от 1 до N включительно). Используя очевидную формулу n!=n*(n-1)!, напишем следующую рекурсивную функцию:

Function Factorial (n:integer):longint;

begin

if n<2 then Factorial:=1

else Factorial:=n*Factorial(n-1);

end;

Тип функции longint использован, так как факториал очень быстро растет и уже 8!=40320, то есть, больше максимального значения типа integer. Эта функция, как нетрудно увидеть, при значении аргумента, меньшем двух, возвращает единицу, а в противном случае возвращает свой аргумент n, умноженный на факториал от n-1. Несложный тест функции мог бы выглядеть так:

begin

writeln ('10!=',Factorial(10));

end.

Рекурсия удобна при составлении различного рода переборных стратегий. Так, игровая задача составления пути по "лабиринту" лучше всего решается рекурсивно – ведь всякий такой путь – это шаг плюс новый путь, уменьшенный на один шаг.

При написании рекурсивных функций следует учитывать, что рекурсивные вызовы довольно быстро заполняют стек и грозят его переполнением. Поэтому основная проблема при написании такой функции – отсечь заведомо лишние вложенные вызовы. Для этого используют дополнительные проверки условий, определяющих, целесообразно ли углублять дальше текущий уровень вложений.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]