Партнерка на США и Канаду по недвижимости, выплаты в крипто

  • 30% recurring commission
  • Выплаты в USDT
  • Вывод каждую неделю
  • Комиссия до 5 лет за каждого referral

Процедуры и функции

В языке Паскаль существуют подпрограммы двух видов: процедуры и функции. Структура подпрограммы аналогична структуре всей программы. Имя подпрограммы обязательно. Заканчивается подпрограмма end; .

Подпрограмма должна быть описана (объявлена) до того, как она будет вызвана (использована) в программе или другой подпрограмме.

Все параметры, используемые в подпрограмме, делятся на локальные и глобальные. Первые объявлены внутри подпрограммы и доступны только ей самой. Вторые объявлены в программе и доступны как программе, так и всем её подпрограммам.

Обмен информацией между основной программой и подпрограммой может осуществляться только с помощью глобальных параметров.

Глобальные параметры могут использоваться в подпрограмме двояким образом:

-  непосредственное обращение к глобальным параметрам (по имени, идентификатору);

-  использование механизмов формальных параметров.

Процедуры

Процедура предназначена для выполнения некоторой законченной последовательности действий. Начинается с заголовка, он обязателен. Состоит из зарезервированного слова procedure, за ним следует имя процедуры, далее в круглых скобках - список формальных параметров.

procedure <имя процедуры> (<список формальных параметров>);

Пример

type

tarr=array [1..100] of integer;

procedure InpInt( var mas:tarr; n:integer);

var

i: integer ; { локальный параметр}

begin

writeln(‘введите ’,n,’ целых чисел’);

for i:=1 to n do

read(mas[i]);

end;

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

НЕ нашли? Не то? Что вы ищете?

Пример Вызов процедуры InpInt для ввода k целых чисел в массив m.

InpInt(m, k);

k - параметр целого типа, m - массив типа tarr.

Функции

Функции предназначены для вычисления какого-либо параметра. Отметим два основных отличия от процедуры

-  заголовок состоит из зарезервированного слова function, за ним в круглых скобках – список формальных параметров, затем через двоеточие записывается тип функции - тип возвращаемого параметра;

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

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

Пример Функция вычисления факториала числа n.

function factorial (n: Byte): longint;

var fact:longint;

i:byte;

begin

fact:=n;

for i:=n-1 downto 2 do

fact:=fact*i;

factorial:=fact;

end;

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

Пример

Part: =sqr (t)/factorial (i),

где

sqr (t) – вызов стандартной функции возведения в квадрат с фактическим параметром t;

factorial (i) - вызов функции, вычисляющей факториал с фактическим параметром i.

Формальные и фактические параметры

Формальные параметры указывают, с какими параметрами следует обращаться к этой подпрограмме (количество, их последовательность, типы). Они задаются в заголовке подпрограммы в виде списка формальных параметров. Список разбит на группы, разделенные точками с запятыми. В группу вкладываются однотипные параметры одной категории.

Все формальные параметры разбиты на 4 категории:

-  параметры-значения (в основной программе эти параметры подпрограммой не меняются);

-  параметры-переменные (эти параметры подпрограмма может изменить в основной программе);

-  параметры-константы (только в Borland Pascal 7.0);

-  параметры-процедуры и параметры-функции (процедурного типа).

Для каждого формального параметра следует указать имя и, как правило, тип.

Для параметра-переменной или параметра-константы – его категории. Имена параметров могут быть любыми, они могут совпадать с именами объектов программы.

В этом случае параметр основной программы с таким именем становится недоступным для непосредственного использования подпрограммой. Тип формального параметра может быть любым, но в заголовке подпрограммы нельзя вводить новый тип.

Пример

Неправильно:

function max (a: array [1..00] of real):real;

Правильно:

type

tArr = array [1..100] of real;

function max(a:tArr):real;

При обращении к подпрограмме формальные параметры заменяются на соответствующие им фактические параметры программы или подпрограммы.

Параметры-значения

Параметры-значения передаются основной программой в подпрограмму через стек в виде их копий. Сам параметр программы подпрограммой измениться не может.

Параметр-значение указывается в заголовке подпрограммы своим именем и - через двоеточие – типом. Значение его может быть любым, кроме файлового типа. Если значений одного типа несколько, то их можно объединить в одну группу, перечислив их имена через запятую, а затем указать их общий тип. Отдельные группы отделяются друг от друга точкой с запятой.

Пример

procedure inp(max, min :real; n :word);

function mult(x, y :integer)real;

В качестве фактического параметра на месте параметра-значения при вызове подпрограммы может выступать любое выражение совместимого для присваивания типа, не содержащее файловую компоненту.

Пример

inp(abs(7),-abs(t), 2*k);

m:=mult(x+y, x-y);

Пример Функция вычисления максимального элемента массива.

В основной программе определен тип-массив, переменная этого типа и переменная целого типа.

type

tArr = array[1..100] of integer;

var

massiv : tArr;

maxim :integer;

function max(mas:tArr; n: byte): integer; {Описание функции}

var ma:integer;

i:byte;

begin

ma:=mas[1];

for i:=2 to N do

if ma<mas[i] then ma:=mas[i];

max:=ma

end;

Тогда возможно

maxim:=max(massiv, 5);

Будет определено максимальное число из пяти первых чисел массива massiv и записано в переменную maxim.

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

Параметры-переменные

При передаче параметров-переменных в подпрограмму фактически через стек передаются их адреса в порядке, объявленном в заголовке подпрограммы. Следовательно, подпрограмма имеет доступ к этим параметрам и может их изменять.

Параметры-переменные указываются в заголовке подпрограммы аналогично параметру-значению, но только перед именем параметра пишется зарезервированное слово var. Его действие распространяется на все переменные, указанные до ближайшей точки с запятой, то есть в пределах одной группы.

Пример

procedure maxmin(a:tArr; var max, min :real; n :word);

Тип параметров-переменных может быть любым, включая файловый.

При вызове подпрограммы на месте параметра-переменной в качестве фактического параметра должна использоваться переменная идентичного типа.

Пример Функция вычисления максимального элемента в массиве.

type tarr = array [1..100] of integer;

{фактический параметр должен быть переменной или типизированной константой типа tarr}

function max (var mas:tarr; n:byte):integer;

var

ma:integer;

i:byte;

begin

ma:=mas[1];

for i:=2 to n do

if ma<mas[i] then ma:=mas[i];

max:=ma;

end;

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

Однако, при такой передаче параметра возможно его нежелательное изменение.

Подпрограмма по-прежнему может работать с одним типом массивов.

Параметры-константы

Условие – параметр передается в подпрограмму, но изменять его подпрограмма не должна. Нежелательно передавать параметр как параметр-переменную. Можно передать как параметр-значение, но если этот параметр имеет большой размер (массив, запись и т. д.), то копии могут занять большую часть стека или даже переполнить его. Это ведет к уменьшению быстродействия программы. В этой ситуации параметр лучше передать как параметр-константу. Такой параметр, если он структурного типа, передается своим адресом, но предусмотрена его защита от изменений (версия 7.0).

Параметр-константа указывается в заголовке подпрограммы аналогично параметру-значению, но перед именем параметра записывается зарезервированное слово const. Действие слова const распространяется до ближайшей точки с запятой, т. е. в пределах одной группы.

Пример

function NewString (const s :string) :string;

Тип параметра-значения может быть любым, кроме файлового.

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

Параметр-константу нельзя передавать в другую подпрограмму в качестве фактического параметра.

Пример Функция вычисления максимального элемента в массиве.

function max(const mas: tarr;n: byte): integer;

var

ma:integer;

i:byte;

begin

ma:=mas[1];

for i:=1 to n do

if ma<mas[i] then ma:=mas[i];

max :=ma;

end;

Пример

const

a: integer = 5;

b: integer = 7;

procedure inc(var c: integer; b: integer)

begin

c:= c+c;

b:= b+b;

writeln(‘удвоенные : ’,c: 5, b: 5);

end;

begin

writeln(‘исходные : ’,a: 5, b: 5);

inc(a, b);

writeln(‘результат : ’,a: 5,b: 5);

end.

исходные :

5

7

удвоенные :

10

14

результат :

10

7

b – параметр-значение

При вызове вычисляется значение, полученный результат копируется во временную память и передается подпрограмме.

Любые возможные изменения в подпрограмме параметра-значения никак не воспринимаются вызывающей программой, т. к. в этом случае изменяется копия фактического параметра.

а – параметр-переменная

При вызове подпрограмме передается сама переменная (адрес переменной). Изменение параметра-переменной приводит к изменению фактического параметра в вызывающей программе.

В случае параметра-константы в подпрограмму также передается адрес области памяти, в которой располагается переменная или вычисленное значение. Компилятор блокирует любые присваивания параметру-константе нового значения в теле подпрограммы.

Пример может служить иллюстрацией механизма «закрывания» глобальной переменной одноименной локальной: хотя b объявлена как глобальная, в теле процедуры её «закрывает» локальная, объявленная как параметр-значение.

Параметры-переменные используются как средство связи с внешним миром: с помощью этих параметров подпрограмма может передавать своей работы вызывающей программе.

Всегда возможна передача результатов и через глобальные переменные.

Оценка вариантов передачи результатов работы подпрограммы вызывающей программе

-  злоупотребление глобальными связями делает программу запутанной, трудной в понимании и сложной в отладке;

-  рекомендуется, где это возможно, использовать передачу результатов через фактические параметры-переменные;

-  с другой стороны, описание всех формальных параметров, как параметров-переменных нежелательно по двум причинам: во-первых, это исключает возможность вызова подпрограммы с фактическими параметрами в виде выражений, что делает программу менее компактной, во-вторых, в подпрограмме возможно случайное использование формального параметра.

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

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

Локализация имен

Вывод подпрограммы осуществляется простым упоминанием имени процедуры в операторе вызова процедуры или именем функции в выражении. Каждая процедура или функция описывается в разделе объявлений (заголовок и тело). В заголовке объявляются – имя и формальные параметры. За заголовком следует тело подпрограммы. В разделе объявления подпрограмм могут быть описаны подпрограммы низшего уровня, в них – описание других подпрограмм (вложенные подпрограммы).

Пример Структура вложенной программы.

program first;

procedure A;

procedure A1;

. . .

begin

. .

end; {A1}

procedure A2;

begin

. .

end; {A2}

begin {A};

end; {A}

procedure B;

procedure B1;

begin

. . .

end; {B1}

procedure B2;

procedure B21;

……………

Все имена, описанные внутри подпрограммы, локализуются в ней, т. е. они «невидимы» снаружи. Другими словами, из основной программы можно обращаться к процедурам А и В, но нельзя вызвать вложенные процедуры А1, А2, В1, В2, В21, В22. Это относиться не только к именам подпрограмм, но и к любым именам, объявленных в них (типам, константам, переменным и меткам). Все имена в пределах подпрограммы, в которой они объявлены, должны быть уникальными и не могут совпадать с именем самой подпрограммы. При входе в подпрограмму низшего уровня становятся доступны не только объявленные в ней имена, но и сохраняется доступ ко всем именам верхнего уровня. Из подпрограммы В21 можно вызвать подпрограмму А, используя имена, объявленные в основных подпрограммах, в подпрограмме В и В2 и затем обращаться к ним.

Пример

program

var v1: . . .;

procedure A;

var v2: . . .;

begin

. . .

end;{A}

procedure B;

var v3: . . .;

procedure B1;

var v4: . . .;

……….

procedure B11;

var v5: . . .;

……………..

Из подпрограммы В11 доступны все 5 переменных v1, v2, v3, v4, v5, из В1 доступно 4 переменных v1, v2, v3, v4. В основной программе доступна всего лишь одна переменная v1.

Основное правило Паскаля

Любая подпрограмма перед использованием должна быть описана. Из В можно вызвать А, но не наоборот (использовать опережающее описание). Подпрограмме доступны только те объекты верхнего уровня, которые описаны до объявления переменных данной программы. Эти объекты являются глобальными по отношению к программе.

В Турбо Паскале допускается произвольная последовательность описания констант, переменных, типов, меток и подпрограмм. Раздел описания переменных var может появляться много раз. Безразличен порядок следования и количество разделов var, const, type, label.

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

Пример

var v1: ...;

procedure S;

var v2: ...;

…………..

end {S};

var v3: ...;

…………..

Из процедуры S можно обратиться к переменным v1 и v2, но нельзя использовать v3, т. к. v3 следует в программе за описанием процедуры S.

Имена, локализованные в подпрограмме, могут совпадать с ранее объявленными глобальными переменными. Считается, что локальное имя «закрывает» глобальное, делая его недоступным.

Пример

var

i: integer;

procedure P;

var

i: integer;

begin

writeln(i);

end {P};

begin

i:=1;

P;

end.

Программа печатает всё, что угодно, так как i – не определено. Если описание var i: integer убрать, то будет напечатано значение глобальной переменной 1.

Области действия идентификаторов

program ShowScope;

область действия x, y, z, Proc1, Proc2

Proc2

Proc2

Proc2

Proc2

 

var x, y, z: real;

procedure Proc1;

(m, n, me: real);

область действия

m, n, me, x, you

 
var x, you: real;

begin {Proc1}

…………

end; {Proc1}

procedure Proc2

(var s, p, x: real);

область действия

s, p, x, Blank, hi, bye

 
const Blank = ' ';

var hi, bye: string;

begin {Proc2}

…………

end; {Proc2}

begin {ShowScope}

………………

end. {ShowScope}

Допустимые вызовы процедуры

Программа или процедура,

откуда производится вызов

Процедуры, которые

могут быть вызваны

ShowScope

Proc1

Proc 2

Proc1, Proc2

Proc1

Proc 1, Proc 2

Вложенные процедуры

program Nested;

область действия

m, n, outer

 
var m, n: real;

procedure outer

область действия

x, y, p, q inner

 
(var x; y: real);

var p, q: real;

procedure inner;

var p, t: real;

область действия

p, t

 
begin {inner}

……………

end; {inner}

begin {outer}

……………

end; {outer}

begin {Nested}

……………

end. {Nested}

Допустимые обращения к идентификаторам

 

Тело программы

или процедуры

Идентификаторы,

к которым возможны обращения

Nested

m, n (глобальные переменные)

оuter (глобальная процедура)

Outer

m, n (глобальные переменные)

x, y (параметры)

p, q (локальные переменные)

оuter (глобальная процедура)

inner (локальная процедура)

Inner

m, n (глобальные переменные)

x, y (параметры outer )

Q (переменная, объявленная в outer)

p, t (локальные переменные)

оuter (глобальная процедура)

inner (процедура, объявленная в outer)

Совместимость и преобразование типов данных

Turbo Pascal – типизированный язык. Концепция типов предполагает, что применяемые в языке операции определены только над операндами совместимых типов.

Совместимость типов учитывается при согласовании формальных и фактических параметров подпрограмм и выполнении операций присваивания.

Несовместимость типов определяется на этапе компиляции. Если типы операндов выражения или формальных и фактических параметров подпрограмм неодинаковы, но совместимы, то производится преобразование типов для приведения их к одному и тому же допустимому типу.

Принадлежат ли переменные одному типу данных, зависит от того, как в языке трактуется эквивалентность. Она может быть структурная и именная.

При первой анализируется структура переменных: число их компонентов и их типы.

Пример

type

t1=array [1..10] of integer;

t2=array [1..10] of integer;

var

v1,v2: t1;

v3: t2;

При структурной эквивалентности Т1 и Т2 – один тип.

В языке Pascal применяется именная эквивалентность. Типы переменных совпадают тогда и только тогда, когда при их описании используется один и тот же идентификатор типа. Т1 и Т2 – 2 разных типа!

Типы Т1 и Т2 идентичны (одинаковы), если

-  Т1 и Т2 – один и тот же тип;

-  определены один через другой, т. е.

type Т1=Т2;

type Т2=Т1;

При вычислении выражений и при выполнении операции присваивания операнды должны быть совместимы.

Во время вычисления выражений два типа операндов совместимы, если:

1)  оба они одного типа;

2)  один – вещественный, другой – целый;

3)  один является диапазоном типа второго операнда;

4)  оба – диапазоны одного и того же базового типа;

5)  оба – множества, составленные из элементов одного и того же базового типа данных;

6)  оба – строки;

7)  один тип – строка, другой – символ.

Пусть Т1 – тип переменной левой части присваивания, Т2 – тип результата выражения его правой части.

Присваивание возможно, если:

1)  Т1 и Т2 – один тип;

2)  Т1 и Т2 – совместимые порядковые типы и значение Т2 лежит в диапазоне возможных значений Т1;

3)  Т1 и Т2 – вещественные типы и значение Т2 лежит в диапазоне возможных значений Т1;

4)  Т1 – вещественный тип, Т2 – целый;

5)  Т1 – строка, Т2 – строка или символ;

6)  Т1 и Т2 – совместимые множества и все элементы Т2 принадлежат множеству возможных значений Т1;

Преобразование типов может быть явным и неявным.

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

Пример

Ord, Odd, Trunc, Round, Chr.

Данные одного типа могут автоматически (неявно) преобразовываться в данные другого типа перед выполнением операций в выражениях и через выполнение операций присваивания. Неявное преобразование типов возможно в трех случаях:

а) в выражениях из вещественных и целочисленных данных целые автоматически преобразуются в вещественные, результат будет вещественного типа;

б) при выполнении операции присваивания переменной вещественного типа значения целого типа значение целого типа преобразуется в значение вещественного типа, после чего оно присваивается переменной левой части оператора присваивания;

в) одна и та же область памяти попеременно содержит данные то одного, то другого типа (совмещение в памяти данных разных типов). Например, при использовании записей с вариантными полями.

Явное преобразование может быть с помощью стандартных функций и с помощью операции приведения типа.

Форма операции

<имя типа>( <имя переменной> / <выражение>);

Операция приведения может находиться, как в левой части оператора присваивания, так и в правой.

Если она находится в левой части оператора присваивания, то в скобках может быть только имя переменной. Если в правой, то и выражение.

Пример Явное преобразование символов и чисел.

uses crt;

var

si:0..255;

a:byte;

c:char;

begin

clcscr;

char(si):=’a’; {Приведение типа в левой части: из чего char

преобразуется для si!!}

writeln(char(si)+’s’); {as}

writeln(si); {97}

si:=ord(‘C’); {Приведение типа в правой части: во что char

преобразуется!!}

writeln(si); {67}

si:=byte (‘E’); {Приведение типа в правой части: во что char

преобразуется!!}

writeln(si); {69}

a:=byte(‘E’);

writeln(a); {69}

char(a):=’B’;

writeln(char(a)); {B}

writeln(a); {66}

byte(c):=si; {Приведение типа в левой части: из чего si

преобразуется для char!!}

writeln(byte(c)+200); {269}

writeln(c); {E}

byte(c):=a;

writeln(c); {B}

end.

Пример Явное преобразование целых и перечислимых типов.

type

Days=(mon, tues, wen, thurs, fri, sat, sun);

var

aDay:Days;

num:byte;

begin

clrscr;

Days(Num):=sun; {Приведение типа в левой части: из чего Days

преобразуется для byte!!}

writeln(ord(Days(num)),ord(sun),num); {666}

aDay:=Days(num); {Приведение типа в правой части: во что byte

преобразуется!!}

writeln(ord(Days(num)),ord(aDay),num); {666}

num:=byte(fri);

writeln(ord(fri), num); {44}

num:=ord(fri);

writeln(Ord(fri), num); {44}

aday:=Days(num+1);

writeln(ord(Days(num+1)), num+1); {55}

num:=byte(pred(fri));

writeln(num); {3}

end.

Бестиповые параметры

Можно использовать параметры-переменные и параметры-константы без указания типов.

Фактическим параметром может быть переменная любого типа; ответственность за правильность использования параметра возлагается на программиста.

Пример

function Equal(var Param1; Param2; Len:word);

Для формальных параметров Param 1, Param 2 в качестве фактических параметров могут быть использованы любые переменные простого типа, массивы, записи и др.

Параметр без типа внутри подпрограммы перед его использованием надо привести (преобразовать) к конкретному определенному типу.

Пример Программа для вычисления максимального значения с использованием функции с бестиповым формальным параметром.

type

tarr = array[1..100] of integer;

var

massiv: tarr;

maxim, i, k: integer;

function max(var mas; n: byte): integer;

type

tarray=array[1..maxint] of integer;

var ma:integer;

i:byte;

begin

ma:=tarray(mas)[1];

for i:=2 to n do

if ma<tarray(mas)[i] then

ma:=tarray(mas)[i];

end;

begin

writeln(‘Введите число элементов массива ’);

read(k);

writeln(‘Введите элементы массива’);

for i:=1 to k do

read(massiv[i]);

maxim:=max(massiv, k);

writeln(maxim);

end.

Параметры-массивы и строки открытого типа

7.0-версия использует открытые параметры-переменные подпрограммы: массивы и строки. Открытые массивы - это параметры-массивы подпрограммы, размер которых не указывается в списке формальных параметров заголовка подпрограммы.

Пример Объявление открытого одномерного массива

function Sum(var a: array of byte): byte;

var в объявлении параметра открытого типа обязательно.

Фактическим параметром открытого массива может быть массив любого размера. Тип элементов фактического массива должен совпадать с типом массива-формального параметра.

В качестве открытых массивов могут использоваться только одномерные массивы. Внутри подпрограмм границы переданного массива могут определяться с помощью функций Low и High.

function Low(x:<тип>):word;

Возвращает минимальное значение величины;

x – параметр или идентификатор порядкового типа, типа-массива, типа-строки или открытого массива.

function High (x:<тип>):word;

Возвращает максимальное значение величины;

x – параметр или идентификатор порядкового типа, типа-массива, типа-строки или открытого массива.

Обращение к Low всегда будет давать 0, а результат функции High – верхнее возможное значение индекса при обращении к массиву относительно нуля, независимо от значений границ массива, передаваемого в подпрограмму.

Напомним, как передавались в подпрограмму массивы.

type

arr=array[1...10] of byte;

…..

procedure P(var a: arr);

begin

…..

end;

Определён в программе тип arr и затем в заголовке подпрограммы объявлена переменная А указанного типа. После такого объявления процедуры Р в качестве в качестве фактического параметра при ее вызове мог фигурировать только массив, описанный при помощи того же самого( из-за именной эквивалентности типа) типа arr, т. е.

var

A1, A2: Arr;

…..

P(A1);

…..

P(A2)

Таким образом, процедура Р может работать только с массивами из десяти элементов типа byte, «пронумерованных» от 1 до 10. Это сильно ограничивает универсальность процедуры.

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

Пример Программа подсчета суммы элементов разных массивов с использованием в процедуре формального параметра-открытого массива.

Возможные пути решения задачи подсчета суммы элементов разных массивов:

1)  написание отдельных суммирующих функций;

2)  использование нетипизированных (бестиповых) параметров;

3)  использование открытых массивов.

program OtkrPar;

type

ta1=array[1..5] of byte;

ta2=array[0..2] of byte;

ta3=array[-2..2] of byte;

const

a1:ta1=( 1, 2, 3, 4, 5);

a2:ta2=( 7, 8, 9);

a3:ta3=( 1, 2, 3, 4, 5);

procedure p(var a: array of byte);

var

i, s:word;

begin

s:=0;

for i:=low(a) to high(a) do begin

s:=s+a[i];

write(‘ ‘, i,’ ‘,a[i]); end;

writeln(‘s= ‘,s);

end;

begin

p(a1); p(a2); p(a3);

end.

Результат выполнения программы program OtkrPar.

Вывод на экран:

4 5 s=15

s=24

4 5 s=15

Программа иллюстрирует интерпретацию граничных значений и индексов открытых массивов.

Остановимся на механизме открытых строк. Во многом они аналогичны открытым массивам и позволяют передавать в подпрограммы строки произвольной длины.

Директива {$V+/-}-проверка строковых параметров.

Если использовать ключ{$V+}, то при обращении к подпрограммам и передаче ей параметров типа string фактические и формальные параметры должны быть идентичными.

Если использовать ключ{$V-}, то формальные и фактические параметры могут быть строками разного типа, в том числе разной длины. Ключ являются локальным.

По умолчанию используется ключ {$V+}. Эквивалентное меню Options/Compiler/Strict var-strings.

В 7.0 версии есть директива {$P+/-} –использование в качестве параметров массивов открытого типа. Она ориентирована специально на поддержку открытых строк. По умолчанию используется ключ{$P-}. Эквивалентное меню Options/Compiler/Open parameters.

Если в программе установили {$P-}, то var-параметры подпрограммы, имеющие тип string, считаются обычными параметрами, передаваемыми по ссылке; для них действительны обычные правила эквивалентности типов.

Если в программе задана директива {$P+}, то var-параметры подпрограмм, имеющие тип string, считаются открытыми параметрами. Это означает, что независимо от установки директивы {$V+/-} в качестве фактического параметра подпрограмме можно передать переменную любого строкового типа.

При этом внутри подпрограммы размер (максимальная длина) строки – формального параметра всегда будет совпадать с размером фактического параметра.

Пример

{$P+}

program TestOpen;

uses crt;

var

s1:string;

s2:string[10];

procedure p(var s: string);

begin

writeln(‘s= ‘,sizeof(s),’ ‘,’l=’, length(s))

end;

begin

s1:=’abs’; p(s1);

s2:=’xxxxxxxxxx’; p(s2);

end.

function size of(x):word;

Возвращает размер аргумента в байтах;

х-переменная любого типа или любой тип.

function length (s:string):integer;

Возвращает текущий размер строки;

S-строка, у которой определяется размер.

Результат выполнения программы program TestOpen.

Вывод на экран:

S=256 L=3

S=11 L=10

Внутри процедуры Р размер открытой строки всегда будет соответствовать реально переданной в ней фактической строки.

Установим {$V-}.

Тогда

S=256 L=3

S=256 L=10

В этом случае внутри процедуры Р строка-параметр всегда трактуется по «максимуму», независимо от того, какой фактический параметр реально ей передан.

По существу, директива {$V-} просто снимает контроль за соответствием типов и за корректностью присваивания строковым var-параметрам, что может привести к тяжелым ошибкам во время исполнения.

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

Дополнение Версия 7.0 содержит специальный стандартный строковый тип с именем openstring, который указывать вместо любого строкового типа при спецификации параметров подпрограмм.

Использование типа openstring включает механизм открытых строк для данного параметра независимо от текущей установки директивы {$P+/-}.

Пример

procedure p(var s: openstring);

begin

writeln(‘s=’, sizeof(s),’L=’,length(s));

end;

Процедурные типы

Turbo Pascal позволяет вводить переменные специального вида, значениями которых могут служить подпрограммы. Иными словами, Turbo Pascal позволяет интерпретировать процедуры и функции как значения, которые можно присваивать переменным и использовать в качестве параметров. Речь идёт о подпрограммах как о целостных объектах, а не о значениях, возникающих в результате их выполнения.

Пример

var

P: procedure;

Возможными значениями переменной P могут являться любые процедуры без параметров.

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

Пример

type

Func=function(x, y: integer): integer;

var

F1, F2: Func;

Переменным F1 и F2 могут быть присвоены в качестве значений функции от двух целых параметров, возвращающие целый результат. Если в этой программе есть описание функции

function Add(a, b: integer):integer;

begin

Add:=a+b

end;

то допустимо присвоение вида F1:=Add;

Переменной F1 в качестве значения присваивается функция как таковая. Выполнения этой функции не происходит.

После такого присваивания имеется возможность вызывать функцию Add как по значению, так и с помощью указания переменной F1, то есть

writeln(Add(1, 2)); writeln(F1(1, 2)); Оба оператора напечатают число 3.

Определение процедурного типа аналогично заголовку подпрограммы с той лишь разницей, что имя подпрограммы не задаётся.

Пример

type

Proc= procedure;

BinOperation= function(x, y: real): real;

UnOperation= function(x: real): real;

Reading= procedure(var f: text; var Elem: char);

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

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

Такие переменные могут быть использованы для вызова подпрограмм, которые присвоены этим переменным.

Пример

Пусть в программе есть

var

operation: function(x, y: real): real;

function Add(a, b: real):real;

begin

Add:=a+b

end;

function Sub(a, b: real): real;

begin

Sub:=a-b

end;

и выполняется

if condition then operation:=Add

else operation:=Sub;

writeln(operation(2.05,3+x));

В зависимости от истинности условия condition переменной operation будут присвоены либо функция Add, либо Sub. Активизируется та функция, которая была присвоена переменной operation.

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

Проблема совместимости

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

Аналогичным образом должны совпадать типы возвращаемых значений в случае функций.

Механизм процедурных типов предоставляет программисту весьма широкие и удобные возможности при разработке программ. Корректная работа требует соблюдения следующих правил.

-  Подпрограмма, присваиваемая процедурной переменной, должна быть оттранслирована в режиме «дальнего типа вызовов». Для достижения эффекта необходимо перед подпрограммой или группой подпрограмм расположить директиву компилятора $F со знаком +, а в конце группы со знаком -.

Пример

{$F+}

function Add (a, b: real):real;

begin

Add:=a+b

end;

function Sub (a, b: real):real;

begin

Sub:=a-b

end;

{$F-}

Если необходимо распространить действие этой директивы на всю программу, то достаточно одну {$F+} поместить в самом начале текста.

Эквивалентом директивы компилятора {$F+} является служебное слово far, которое должно быть записано перед блоком подпрограммы. (другой - «близкий» тип вызова задаётся служебным словом near).

function add (a, b: real):real; far;

begin

Add:=a+b

end;

-  Подпрограмма, присваиваемая процедурной переменной, не должна быть стандартной процедурой или функцией. Это ограничение при необходимости можно легко обойти, заключив вызов стандартной подпрограммы в «оболочку», после чего её можно использовать в качестве присваивания.

Пример

var

Func: function (s: string): byte;

….......

function Mylength (s: string): byte; far;

begin

Mylength:=length(s)

end;

………

Func:= Mylength;

-  Важное ограничение – обсуждаемые подпрограммы не могут быть вложенными в другие подпрограммы.

-  Подпрограммы, присваиваемые процедурным переменным, не могут быть подпрограммами специального вида, содержащими спецификации interrupt и конструкцию inline.

Можно описывать процедуры и функции, параметрами которых являются процедурные переменные.

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

Пример

program Tables;

type

func= function(x, y: integer): integer;

function Add(a, b: integer):integer; far;

begin

Add:=a+b;

end;

function Mult(a, b: integer): integer; far;

begin

Mult:=a*b;

end;

procedure MakeTable(w, h:integer; operation: func);

var

i, j:integer;

begin

write(‘ ‘);

for i:=1 to w do write(‘_ _ _ _ _ ‘);

writeln;

for i:=1 to h do begin

write(i: 5,’ | ‘);

for j:=1 to w do

write(operation(j, i): 5);

writeln;

end;

writeln; end;

begin

Make_table(10,10, Add);

Make_table(10,10, Mult);

end.

Два вызова процедуры MakeTable, которая строит таблицы, используя для вычисления в первом случае функцию Add, во втором – Mult.

Переменные процедурных типов можно передавать в качестве параметров подпрограммы.

Функции, возвращающие значения процедурных типов, не допускаются.

В общем случае использование процедурной переменной в операторе или выражении означает вызов присвоенной данной переменной процедуры или функции.

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

Пример

type

IntFunc= function: integer;

var

f: IntFunc;

n: integer;

function ReadInt: integer; far;

var

i: integer;

begin

read(i);

ReadInt:=i;

end;

begin

f:= ReadInt; {присваивание значения процедуры}

n:= Readint; { присваивание результата функции}

end.

Первый оператор в основной программе присваивает значение процедуры Readint (её адрес) процедурной переменной F, второй вызывает функцию Readint и присваивает полученное значение переменной n. Полученные значения процедуры или вызов функции различаются по типу присваиваемой переменной(f или n).

Дополнение Директивы подпрограмм дают информацию о размещении подпрограмм.

1.  FORWARD - директива заголовка подпрограммы описанной дальше по тексту.

2.  FAR, NEAR - директивы формирования «дальнего» адреса и «ближнего» адреса.

3.  EXTERNAL - директива внешней подпрограммы.

4.  ASSEMBLER - директива подпрограммы, написанной на ассемблере.

5.  INLINE - директива, позволяющая включить в программу часть, написанную на ассемблере.

6.  INTERRUPT – директива процедуры обработки прерываний.

(См. пункт 10.5.

, Епанешников в среде Turbo - Pascal 7.0. М.: Диалог - МИФИ. 20с.)

Рекурсия

В теле подпрограммы известны (доступны) все объекты, описанные в блоке, в том числе и имя самой подпрограммы. Таким образом, внутри тела подпрограммы возможен вызов самой подпрограммы. Процедуры и функции, использующие вызовы “самих себя”, называются рекурсивными. Допустима косвенная рекурсия, при которой, например, процедура А вызывает процедуру В, а та, в свою очередь, вызывает процедуру С, которая вызывает первоначальную процедуру А.

Пример Вычисление факториала

0!=1; 1!=1; n!=1*2*3… (n-1)*n n!=(n-1)!*n.

function Fact (n: word): longint;

begin

if n=1 then

Fact:=1

else

Fact:=n*Fact(n-1)

end;

В языке Pascal нет никаких ограничений на рекурсивные вызовы подпрограмм. Необходимо только хорошо понимать, что каждый очередной рекурсивный вызов приводит к образованию новой копии локальных объектов подпрограммы. Все эти копии, соответствующие цепочке активизированных и незавершённых рекурсивных вызовов, существуют независимо друг от друга.

Побочный эффект

Пример

program SideEffect;

var

a, z: integer;

function change(x: integer):integer;

begin

z:=z-x;

change: =sqr(x)

end;

begin

z:=10; a:=change(z);

writeln(a, z);

z:=10; a:=change(10)*change(z) {вызывается первым!!} writeln(a, z);

z:=10; a:=change(z)*change(10);

wrireln(a, z);

end.

Результат выполнения программы program SideEffect.

Вывод на экран

100  0

10

0  0

Анализ возможных вариантов работы программы представлен в таблице. Результат сравнения результатов анализа и результата работы программы program SideEffect показывают, что первым осуществляется вызов change(z), а затем change(10).

Непосредственное использование глобальных переменных в подпрограммах может привести к нежелательным изменениям их значений. Контроль за непосредственным использованием глобальных переменных в подпрограммах осуществляется программистом.

значения после

a

z

1 вызова

100

0

вывод на экран

100

0

возможный вариант

первым вызывается change(10)

2 вызова

100

0

3 вызова

0

0

вывод на экран

0

0

первым вызывается change(z)

2 вызова

100

0

3 вызова

100

-10

вывод на экран

10000

-10

возможный вариант

первым вызывается change(10)

4 вызова

100

0

5 вызова

0

0

вывод на экран

0

0

первым вызывается change(z)

4 вызова

100

0

5 вызова

100

-10

вывод на экран

10000

-10

Рекурсивная функция

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

Итеративное – повторяющееся действие.

Пример Умножение M*N.

Компьютер умножает на 1 и складывает и только!! Предположим, что необходимо выполнить на этом компьютере умножение 6*3.

Эта задача может быть разделена на две:

1.  задача 6*2;

2.  прибавить 6 к результату 1 задачи.

Вторую задачу можно выполнить. Первую задачу разделим на две:

1.1.  умножить 6*1;

1.2.  прибавить к результату задачи 1.1.

Названные действия представим в виде:

Multiply: =M + Multiply (M, N-1) - рекурсивный шаг.

умножение M*(N-1)

сложение M с результатом предыдущей задачи

Если N=1, то Multiply : = M - конечный шаг.

Построим рекурсивную функцию.

function Multiply (M, N: integer): integer;

{Умножение выполняется с помощью оператора +.

Предусловие: M и N определены и N>0

Постусловие: возвращение M*N}

begin

if N=1 then

Multiply: = M {конечный шаг}

else Multiply: = M + Multiply (M, N-1) {рекурсивный шаг}

end;

Трассировка рекурсивной функции

Пусть в программе есть вызов Multiply (6, 3);

Определение Запись активации – прием, позволяющий проследить значения параметров при каждом вызове и составить общую картину выполнения функции.

При вызове функции Multiply M=6, N имеет значения 3, 2 и 1.

Свойства рекурсивных задач и решений

1.  В задаче можно выделить один или больше конечных шагов, имеющих простое, не рекурсивное решение.

2.  Все прочие шаги задачи могут быть разделены на задачи (с помощью рекурсии), которые приближаются к конечным шагом.

3.  В конце концов вся задача может быть сведена таким образом только к конечным шагом, имеющим сравнительно простое решение.

Последовательность действий для решения задачи

1.  Вникнуть в задачу.

2.  Определить в задаче конечные цели.

3.  Определить в задаче рекурсивные цели.

Обычно рекурсивные алгоритмы содержат оператор if, имеющий следующую форму.

if достигнут конечный шаг

then

этот шаг выполняется

else

задача с помощью рекурсии делится на более простые подзадачи.

Пример Multiply (5,4)

Умножить(5*4) Умножить (5*3) Умножить (5*2) Умножить (5*1)

добавить 5 добавить 5 добавить 5

к (5*3) к (5*2) к (5*1)

Рекурсивная процедура

procedure Reverse (N: integer);

{выводит строку длиной N в порядке, обратном тому, в котором эта строка вводилась.

Предусловие: N больше или равно единице.

Постусловие: выводит N символов}

var

next: char; {следующий символ}

begin

if N=1 then

begin {конечный шаг}

read (next);

write (next)

end

else

begin {рекурсия}

Read (next)

Reverse (N-1)

Write (next)

end

end;

Трассировка выполнения оператора REVERSE (3)

Значение N передается в процедуру при её вызове, так как N – её параметр.

Значение Next первоначально не определено, так как Next – локальная переменная.

Рекурсивный спуск завершается и начинается рекурсивный возврат, когда условие завершения становится истинным.

Последовательность действий для рекурсивного вызова Reverse (3)

Действия из одной записи активации представлены с одинаковым отступом.

Вызов Reverse со значением N, равным 3.

Считывание в NEXT первой буквы a.

Вызов Reverse со значением N, равным 2.

Считывание в NEXT второй буквы b

Вызов Reverse со значением N, равным 1.

Считывание в NEXT третьей буквы c.

Вывод третьей буквы c.

Возврат из третьего вызова.

Вывод второй буквы b.

Возврат из второго вызова.

Вывод первой буква a.

Возврат из первого вызова.

Стеки для локальных переменных и параметров

Stack - груда, куча, кипа, стопка (перевод с англ.) Структура данных - сравнение со стопкой тарелок. Когда имеет место очередной вызов такой рекурсивной процедуры, значение параметра, ассоциированное с этим вызовом, помещается на верх стека значений. При этом новая ячейка памяти (для следующего значения), содержимое которой изначально не определено, присоединяется к стеку поверх только что занесенного туда значения. Например, если имеет место обращение к значению N или next в процедуре Reverse, то используется соответствующее значение с верха стека. Если же имеет место рекурсивный возврат, значение, находящееся в данный момент на верху стека, удаляется и следующее значение перемещается на верх стека.

Состояние стеков при вызовах процедуры Reverse

После первого вызова процедуры Reverse

N

next

3

?

Непосредственно перед вторым вызовом процедуры Reverse буква а считывается в стек next

N

next

3

a

После второго вызова Reverse

N

next

2

?

3

a

Непосредственно перед третьим вызовом процедуры Reverse буква b считывается в стек next

N

next

2

b

3

a

После третьего вызова Reverse

N

next

1

?

2

b

3

a

В ходе выполнения процедуры в стеке next считывается c и тут же печатается, так как N в этот момент имеет значение 1(конечный шаг).

N

next

1

c

2

b

3

a

При рекурсивном возврате значения на верху стека удаляются

После первого возврата

N

Next

2

b

 

3

a

 

Управление возвращается оператору write, осуществляющему вывод значения b, находящегося в этот момент на верху стека next.

После второго возврата

N

Next

3

a

Управление передается оператору write, при осуществляется вывод значения c, находящегося на верху стека next.

После третьего возврата:

N

Next

?

?

В Паскале эти действия выполняется автоматически.

Реализация стеков параметров в Паскале

Обсуждали отдельные стеки для N и next. В действительности компилятор поддерживает единый стек. Каждый раз, когда имеет место вызов процедуры или функции, все её параметры и локальные переменные заносятся в стек вместе с адресами памяти операторов вызова. Последние позволяют компьютеру осуществить возврат после выполнения процедуры или функции. Хотя в стеке могут содержаться множество копий параметров процедуры, в то же время в памяти присутствует единственная копия тела процедуры.