Процедуры и функции в языке Паскаль. Сфера действия описаний
Процедуры и функции в языке Паскаль. Сфера действия
описаний
В
языке Паскаль (как вы уже поняли из предыдущего материала) существуют понятия
процедуры и функции. Процедуры и функции можно определить как замкнутые
программные единицы, реализующие некоторый алгоритм. Фактически процедура или
функция - это почти программа, почти - потому что она не может выполняться
самостоятельно, а всегда вызывается какой-то другой процедурой или функцией.
Программы, которые мы до сих пор писали, тоже были процедурами, правда,
несколько особенными - главными процедурами. Программа может содержать любое
количество процедур и функций, но она всегда содержит одну и только одну
главную процедуру, с которой начинается выполнение программы.
Структура
процедуры или функции очень похожа на структуру главной процедуры, она также
содержит раздел описаний и раздел операторов; раздел операторов начинается с
BEGIN и заканчивается END; (но не END. - как у главной процедуры). Единственным
новым оператором для вас будет оператор заголовка, с которого начинается всякая
процедура и функция. Все процедуры и функции записываются в разделе описаний
какой-либо другой процедуры или функции, в том числе и главной процедуры.
Оператор заголовка процедуры имеет вид:
PROCEDURE имя ( список параметров ) ;
Здесь
имя - имя процедуры (любой идентификатор), список параметров может
отсутствовать, но если он есть, записывается в круглых скобках после имени
процедуры и имеет вид :
[VAR] имя , ... имя : тип ;
...........................
[VAR] имя , ... имя : тип
Здесь
имя - имена параметров, каждый параметр может использоваться внутри процедуры
как обычная переменная соответствующего типа. Тип - имя типа, но не описание
пользовательского типа; скажем, описание параметра в виде x:1..5 неверно, но,
если выше описан соответствующий тип: TYPE MyType=1..5, то параметр можно
описать в виде x:MyType. Ключевое слово VAR перед описанием параметров означает
в данном случае, что все параметры до ";" или до ")" -
параметры-переменные; если же VAR отсутствует, то параметры являются
параметрами-значениями. Смысл этих понятий мы рассмотрим несколько позже.
Процедуры
вызываются в других процедурах и функциях с помощью уже известного вам
оператора вызова:
имя ( список аргументов );
Список
аргументов задается в том и только в том случае, когда в заголовке процедуры
задан список параметров. Аргументы в списке разделяются запятыми и могут быть
любыми выражениями, если соответствующий параметр есть параметр-значение, или
только именами переменных, если соответствующий параметр есть
параметр-переменная. Количество аргументов всегда должно совпадать с
количеством параметров, и тип аргумента должен быть таким же, как тип
параметра. При вызове процедуры значение соответствующих аргументов передается
параметрам, и таким образом процедура получает информацию из вызывающей
процедуры или функции. Запишем программу, использующую процедуру, которая будет
аккуратно выводить значение переменной :
PROCEDURE OutVar(x:Real; Name:Char);
BEGIN WRITELN('Переменная ',Name,' равна
',x); END;
VAR a,b,c,d : Real;
BEGIN WRITE('Введите переменные a,b,c,d '); READ(a,b,c,d);
OutVar(a,'a'); OutVar(b,'b');
OutVar(c,'c'); OutVar(d,'d');
END.
Наша
процедура OutVar получает из главной процедуры вещественное число x и символ
Name, но ничего не передает обратно. Теперь попробуем написать процедуру,
которая по заданным значениям x и y вычисляет cos(x)+cos(y) и cos(x)-cos(y) :
PROCEDURE T(x,y:Real; Cplus,Cminus:Real);
BEGIN Cplus:=cos(x)+cos(y); Cminus:=cos(x)-cos(y); END;
VAR p,m:Real;
BEGIN T(1.235,0.645,p,m); WRITELN(p:7:3,m:7:3); END.
Запустим
эту программу и - вместо правильного результата 1.129,-0.470 - получим в лучшем
случае нули. Дело в том, что через параметры-значения (а Cplus и Cminus описаны
в нашей процедуре как параметры-значения!) невозможно передать информацию из
процедуры, но лишь в процедуру. Чтобы правильно решить нашу задачу, следует
Cplus и Cminus описать в заголовке как параметры-переменные:
PROCEDURE T(x,y:Real; VAR Cplus,Cminus:Real);
BEGIN Cplus:=cos(x)+cos(y); Cminus:=cos(x)-cos(y); END;
Таким
образом, входные параметры процедуры могут быть и параметрами -значениями и
параметрами-переменными, а выходные параметры - только параметрами-переменными.
Для того, чтобы глубже понять это правило, выясним, что же происходит с
параметрами и аргументами при вызове процедуры. В момент вызова для каждого
параметра-значения в специальной области памяти, называемой стеком (за контроль
переполнения стека отвечает описанная выше опция компилятора Stack cheking),
создается его копия - переменная соответствующего типа, которой присваивается
значение аргумента. В дальнейшем процедура работает с этой копией, и при выходе
из процедуры копия уничтожается. Таким образом, никакие изменения
параметра-значения не могут быть известны за пределами процедуры. По-другому
обрабатываются параметры-переменные: в процедуру передается не значение
аргумента, а его адрес, и она работает с аргументом (теперь понятно, почему
аргумент, соответствующий параметру-переменной, должен быть только именем
переменной: он должен иметь адрес). Так что все изменения параметра на самом
деле происходят с аргументом и известны в вызывающей процедуре.
Функция,
в отличие от процедуры, всегда вычисляет некоторое значение скалярного типа,
которое внутри функции должно быть присвоено имени функции. Заголовок функции
имеет вид:
FUNCTION имя ( список параметров ) : тип ;
В
остальном функции аналогичны процедурам. Обращение к функции осуществляется с
помощью указателя функции:
имя ( список параметров )
Указатель
функции может использоваться как и любое другое выражение того же типа, но это
не оператор, в отличие от оператора вызова. Запишем пример функции:
FUNCTION Na3(x:LongInt):Boolean;
{
функция проверяет, делится ли x на 3 }
BEGIN Na3:=x MOD 3=0; END;
VAR L:LongInt;
BEGIN WRITE('Введите целое число '); READ(L);
WRITE('Число ',L);
IF NOT Na3(L) THEN WRITE(' не');
WRITELN(' делится на 3 !');
END.
В
любой процедуре и функции можно использовать черезвычайно полезную стандартную
процедуру Exit без параметров для немедленного выхода в вызывающую процедуру.
Все
процедуры и функции Паскаля являются рекурсивными, то есть могут вызывать сами
себя, никаких усилий со стороны программиста для этого не требуется. В качестве
примера запишем функцию, вычисляющую n!
FUNCTION Factorial(n:Byte):Real;
BEGIN IF n<=1 THEN Factorial:=1
ELSE
Factorial:=n*Factorial(n-1);
END;
Но
это, конечно, очень плохая функция, гораздо лучше записать этот алгоритм так:
FUNCTION Factorial(n:Byte):Real;
VAR i:Byte; f:Real;
BEGIN f:=1; FOR i:=2 TO n DO f:=f*i;
Factorial:=f;
END;
Итак,
мы знаем, что программа может содержать много процедур и функций, и в каждой из
них могут быть описаны типы, константы и переменные. Но не все из них могут
быть использованы в любом месте программы, каждое описание имеет строго
определенную сферу действия. Пусть процедура А находится внутри процедуры В -
условимся называть процедуру А внутренней по отношению к В, а процедуру В -
объемлющей по отношению к А. Если же ни процедура А не находится внутри В, ни В
не находится внутри А, то эти процедуры - внешние по отношению друг к другу.
Сфера действия описания любого объекта включает ту процедуру, где он описан
(начиная с места описания) и все внутренние процедуры, если там данный
идентификатор не описан. В принципе, это дает возможность передавать информацию
в процедуры и функции, минуя параметры, то есть пользоваться во внутренней
процедуре переменными, описанными в объемлющей процедуре, но такой стиль
программирования считается ненадежным. Старайтесь, если это возможно, все
переменные, используемые в процедуре, описывать в этой процедуре.
Для
чего нужны процедуры и функции, когда и как их следует применять? Многие
начинающие программисты избегают процедур и функций, утверждая, что "без
них проще". На самом деле обойтись без функций и процедур легко только в
самых тривиальных программах. Сколько-нибудь сложная программа, записанная
"одним куском", требует при отладке от программиста огромных усилий,
которые зачастую все равно пропадают даром. Обязательно используйте в своих
программах процедуры и функции! Хорошая программа должна содержать главным
образом обращения к процедурам и функциям. Конечно, не существует никаких
жестких правил, определяющих, когда использовать функции, а когда нет, но автор
этой книжки может предложить несколько нестрогих, но полезных рецептов:
-
выделяйте в процедуру (функцию) небольшой логически завершенный фрагмент
алгоритма;
-
не смешивайте в одной процедуре (функции) ввод-вывод данных и вычислительные
алгоритмы;
-
называйте свои процедуры (функции) мнемоническими именами;
-
если алгоритм, который вы решили выделить в процедуру (функцию), все еще
слишком сложен, оформите фрагмент этого алгоритма в другой процедуре (функции);
-
если алгоритм должен вычислить одно скалярное значение, пусть это будет
функция, а не процедура;
-
если в вашей программе встречаются многократно вложенные циклы или
"многоэтажные" условные операторы, это верный признак, что вам нужны
процедуры (функции);
-
если текст вашей программы не умещается на одном экране - подумайте о
процедурах;
-
используйте в процедурах и функциях процедуру Exit.
Список литературы
Для
подготовки данной работы были использованы материалы с сайта http://elib.albertina.ru/