В приведенных выше примерах часть переменных описана в блоке описания переменных программы. Такие переменные называются глобальными. Они существуют в любой момент выполнения программы. Другая часть переменных описана внутри процедуры или функции. Это — локальные переменные. В отличие от глобальных они создаются не в момент запуска программы, а в момент каждого очередного вызова процедуры, и после окончания работы процедуры уничтожаются. (Для повышения эффективности работы часть процедур и локальных переменных не уничтожается полностью, а хранится в стеке. Это дает возможность быстро вызывать их. Однако с точки зрения программиста это совершенно не меняет ситуации с их "существованием" и правилами видимости). Поэтому эти переменные и называются локальными: они существуют только внутри соответствующей процедуры. "Снаружи" процедуры локальные переменные этой процедуры не имеют смысла, и попытка вызова такой переменной приведет к сообщению компилятором об ошибке. Говорят, что локальная переменная невидима вне своей процедуры. С другой стороны, глобальные переменные видимы в любом месте программы т.е. их можно вызывать и внутри процедур и функций. Поэтому их называют глобальными переменными. Глобальные переменные иногда используются для передачи данных внутрь процедуры не через список параметров в заголовке процедуры, а "напрямую". Как правило этот путь нельзя считать хорошим, т.к. обычно он не соответствует принципам структурного программирования. С другой стороны, в ряде случаев бывает целесообразно менять значение некоторых нелокальных переменных в процессе работы процедуры, т.е. получать так называемый побочный эффект работы процедуры. Этот способ часто используется в объектном программировании для приведения в соответствие значений нескольких взаимно зависимых параметров.
2.14. Пример на правила видимости и передачи параметров
unit factorials;
uses . . . ;
var
k,n,m: integer; {глобальные целые переменные}
p:extended;
ch: char; {глобальная литерная переменная}
function fact1(n:integer): extended;{процедура-функция, передача
входного параметра n по значению}
var i: integer;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
end;
procedure fact2;{процедура, передача входного параметра через
глобальную переменную n, возврат результата через
глобальную переменную p}
var i: integer;
result:extended;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
p:=result;
end;
procedure fact3(n:integer; var r:extended);{процедура, передача
входного параметра n по значению,
возврат результата через параметр r по имени}
var i: integer;
result:extended;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
r:=result;
end;
function fact4(n:integer):extended; {процедура-функция, передача
входного параметра n по значению, побочный
эффект – изменение глобальной переменной m}
var
i: integer;
begin
if n=0 then n:=1;
result:=1;
for i:=1 to n do result:=result*i;
fact4 =result;
m:=m+1;{побочный эффект}
end;
begin {тело основной программы}
k:=…;
m:= 0;n:=…;p=…;
case ch of
'1' : p := fact1( n );
'2' : fact2 ;
'3' : fact3( n , p );
'4' : p := fact4( n );
end;
……
End.
2.15. Рекурсия
Очень часто в математических выражениях используется так называемое рекурсивное определение функций:
factorial(1)=1; для n>1: factorial(n)= factorial(n-1)*n.
Многие алгоритмы задаются рекурсивно. Например, анализ транслятором вложенных процедур. Для реализации такого рода алгоритмов используется рекурсивный вызов процедур. Например, вычисление факториала можно задать как
function factorial(n:integer):extended;
begin
result:=1;
if n>=1 then result:=n*factorial(n-1)
end;
У рекурсии есть недостатки: а) при каждом вложенном вызове создается копия локальных переменных процедуры, они храняться в стеке. При большой глубине вложенности стек может переполниться; b) очень часто встречается ситуация бесконечной рекурсии. Так, если бы мы не написали условия n>=1, рекурсные вызовы продолжались бы вплоть до переполнения:
function factsfoo(n:integer):extended;
begin
result:>=n*factorial(n-1)
end;
Основные принципы объектно-ориентированного программирования (ООП) на примере языка Object Pascal (Delphi) со статической версией объектной модели
Рассматриваемая версия объектной модели характерна для языка TurboPASCAL (TP5.5 – TP7.0) и очень близка к объектной модели C++ ( за исключением того, что в C++ поддерживается также множественное наследование и ряд других дополнительных возможностей). В Delphi эта модель поддерживается, но рассматривается как устаревшая. Основной считается модель с динамически создаваемыми объектами, во многом близкая по идеологии к объекной модели языка Java.
2.1. Инкапсуляция. Объект. Поля данных и методы объекта
Инкапсуляцией называется объединение каких-либо относительно независимых элементов в единое целое. В стандартном PASCAL существует тип record — "Запись":
type
tLocation=
record {поля записи}
X,Y:Integer
end;
var Location1,Location2:tLocation;
a:integer;
Поля записи — самостоятельные переменные. Доступ к ним — через квалификацию с помощью точки (составное имя с разделительной точкой). К полям можно обращаться по отдельности. Например, присвоить переменной a значение поля X:
a:=Location1.X
Либо изменить значение поля Y:
Location1.Y:=212
А можно действовать с инкапсулированными в запись полями как с единым целым:
Location2:=Location1
при этом полю X записи Location1 будет присвоено значение X записи Location2, и, аналогично, полю Y в Location1 — значение Y записи Location2.
Развитием идеи объединения (инкапсуляции) различных полей стали объекты.
Пример:
объект:
точка на экране tDot;
ее данные:
координаты X и Y — целые переменные;
светится или нет на экране active — булевская переменная;
ее методы действия:
"нарисовать": Show;
"скрыть": Hide;
"передвинуть по X и Y на величины dX и dY": Move(dX,dY)
"передвинуть в точку с координатами X и Y": MoveTo(X,Y)
Использование в программе:
var aDot:tDot;
...
aDot.X:=10; {координате X точки Dot присвоить значение 10}
aDot.Y:=20;
aDot.Show; {вызвать метод показа точки Show; говорят: "точка себя рисует"}
Описание методов:
procedure tDot.Show;
begin
… {рисование точки текущим цветом пера}
active:=true;
end;
procedure tDot.Hide;
begin
… {рисование точки цветом фона}
active:=false;
end;
procedure tDot.Move(dX,dY:Integer);
begin
Hide;
X:=X+dX;
Y:=Y+dY;
Show;
end;
3.2 Задание модуль класса (статическая объектная модель)
Теперь мы можем наметить, как будет выглядеть модуль с нашим классом:
Unit myFigure;
uses …;
interface
type
tDot=
object
X,Y:Integer;
active:Boolean;
procedure Show;
procedure Hide;
procedure MoveBy(dX,dY:Integer); {сдвиг на dX,dY}
procedure MoveTo(NewX,NewY:integer);{рисование в точке NewX,NewY}
end;
tCircle=
object(tDot)
...
end;
implemetation
{——— раздел реализации ———}
procedure tDot.Show;
begin
...
end;
procedure tDot.Hide;
begin
...
end;
procedure tDot.MoveBy(dX,dY:Integer);
begin
...
end;
procedure tDot.moveTo(X,_,Y_:integer);
begin
…
end;
begin
{раздел инициации. Всегда делать пустым!!!}
end.