Сделай Сам Свою Работу на 5

Процедуры с объявлениями.





ПРОЦЕДУРЫ CF Pascal

 

Процедуры CF Pascal на самом деле обладают значительно большей гибкостью и мощью, чем было представлено в главе 2. Процедуры могут иметь параметры, могут объявлять внутренние переменные, чьи значения доступны только внутри процедуры, и таким образом защищены от использования извне. Для иллюстрации использования процедур будет разработана небольшая библиотека операций над натуральными числами.

 

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

В некотором смысле процедура реализует идею функции значения части программы.

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



 

Процедуры с параметрами

 

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

Понятия: оператор процедуры, объявление процедуры, формальные параметры, фактические параметры, правило «все или ничего»

 

Простые процедуры и параметры.

Процедуры, описанные в главе 2 и их формальное значение, описанное в главе 6 позволяют объявить оператор BEGIN и потом вызывать его через процедурное выражение используя только идетификатор процедуры. Объявление добавляет идентификатор в состояние выполнения, значением которого является текст тела процедуры.

Например, объявление процедуры:

 

PROCEDURE SwitchOneTwo;

BEGIN

Hold := OneV;

OneV := TwoV;

TwoV := Hold;

END.

 

Может быть написано для обмена значений OneV и TwoV, разрушая значение Hold. Это объявление добавляет пару



<SwitchOneTwo, Hold := OneV; OneV := TwoV; TwoV := Hold>

к состоянию выполнения и процедурное выражение SwitchOneTwo имеет значение

SwitchOneTwo = <s, s(SwitchOneTwo) (s)>

= <s, Hold := OneV; OneV := TwoV; TwoV := Hold (s)>

= (Hold, OneV, TwoV := OneV, TwoV, OneV)

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

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

 

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

Например, заголовок:

 

PROCEDURE SwitchThese2(VAR V1, V2, temp: CHAR)

 

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

 

PROCEDURE SwitchThese2(VAR V1, V2, temp: CHAR);

BEGIN

Temp := V1;

V1 := V2;

V2 := Temp;

END

 

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



Например, если в программе объявлены переменные Hold, OneV и TwoV, то процедурное выражение

 

SwitchThese2(OneV, TwoV, Hold)

 

Выполнит обмен значений OneV и TwoV точно так же как это сделала бы процедура SwitchOneTwo. Но мы также с помощью

 

SwitchThese2(OneV, AnyV, Hold)

 

сможем обменять значения OneV и AnyV без всяких изменений в объявлении переменной Switchthese2. Как будто в тексте объявления процедуры формальные параметры были заменены на фактические. В последнем примере

 

SwitchThese2(OneV, AnyV, Hold)

= Hold := OneV; OneV := AnyV; AnyV := Hold

= (Hold, OneV, AnyV := OneV, AnyV, OneV)

 

Процедуры могут иметь параметры разных типов. Например:

 

PROCEDURE Copy (VAR F1, F2: TEXT; VAR Ch: CHAR);

 

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

 

 

PROCEDURE Copy (VAR F1, F2: TEXT; VAR Ch: CHAR);

BEGIN {Copy}

WHILE NOT EOF (F1)

DO

BEGIN

WHILE NOT EOLN (F1)

DO

BEGIN

READ(F1, Ch);

WRITE(F2, Ch)

END

READLN(F1);

WRITELN(F2)

END

END {Copy}

 

Эта процедура может в дальнейшем быть использована с любым существующими файлами, которые подставляются вместо F1 и F2 и любой существующей символьной переменной, которая подставляется вместо Ch. Например:

 

Copy(INPUT, OUTPUT, temp)

 

Будет отображать input в output.

 

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

Введение процедур с параметрами требует изменений в синтаксических правилах CFPascal.

 

SR 27. <процедура> ::= <заголовок процедуры>; <оператор BEGIN>

 

SR 28. <оператор процедуры> ::= <идентификатор>

| <идентификатор>(<список фактических параметров>)

SR 30. <список фактических параметров> ::= <список идентификаторов>

 

SR 31. <заголовок процедуры> ::= PROCEDURE <идентификатор>

| PROCEDURE <идентификатор>(<список формальных параметров>)

 

SR 32. <список формальных параметров> ::= VAR <список идентификаторов> : <тип>

| <список формальных параметров>; VAR <список идентификаторов> : <тип>

 

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

 

CR4.Идентификатор процедуры, использованный в <операторе процедуры> должен появиться в <заголовке процедуры> при объявлении <процедуры>, <список фактических параметров> должен быть такой же длины что и <список формальных параметров> соответствующего <заголовка процедуры>. Типы идентификаторов в <списке формальных параметров> и <списке фактических параметров> должны соотвествовать.

 

CR6.Любой идентификатор, объявленный в<списке формальных параметров> процедуры может быть использован только в <операторе BEGIN> данной процедуры. Такие идентификаторы называются локальными.

 

Разделение файлов.

В разделе 4.1.5 была разработана программа для копирования данных из INPUT в OUTPUT с разделением на нечетные (odd), которые печатались первыми и четные (even).

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

Проект программы был такой:

PROGRAM Split(INPUT, OUTPUT);

{Копировать INPUT to OUTPUT, сначала нечетные, потом четные}

VAR

Odds, Evens: TEXT;

BEGIN {Split}

{Разделить INPUT на четные и нечетные}

{Копировать нечетные в OUTPUT}

{Копировать четные в OUTPUT}

END. {Split}

 

Две задачи

{Копировать нечетные в OUTPUT}

{Копировать четные в OUTPUT}

Могут быть выполнены одной процедурой CopyOut, если ей задать параметр, вместо которого будут поставлены либо Odds, либо Evens.

 

PROGRAM Split(INPUT, OUTPUT);

{Копировать INPUT to OUTPUT, сначала нечетные, потом четные}

VAR

Odds, Evens: TEXT;

PROCEDURE CopyOut(VAR F1: TEXT; VAR Ch1: CHAR);

{Копирует F1 в OUTPUT, игнорируя маркеры конца строк}

BEGIN {Split}

{Разделить INPUT на четные и нечетные}

CopyOut(Odds, Ch);

CopyOut(Evens, Ch);

END. {Split}

 

Вот определение CopyOut

 

PROCEDURE CopyOut(VAR F1 : TEXT; VAR Ch1 : CHAR);

{Копирует F1 в OUTPUT, игнорируя маркеры конца строк}

BEGIN {CopyOut}

RESET(F1);

WHILE NOT EOF(F1)

DO

BEGIN

WHILE NOT EOLN(F1)

DO

BEGIN

READ(F1, Ch);

WRITE(Ch)

END;

READLN(F1)

END

END {CopyOut}

Оператор BEGIN процедуры CopyOut использует только параметры F1 и Ch, объявленные в заголовке процедуры. Напротив, процедура без параметров может использовать переменные объявленные в теле программы, содержащей процедуру. Процедура с параметрами может использовать смешанный подход – одновременно пользоваться переменными окружающей программы и параметрами. Однако, процедуры, использующие только собственные параметры более легки для понимания и повторного использования. Эта идея использования одних только параметров в процедурах получила название правила «все или ничего».

 

Исчисление параметров.

 

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

 

PROGRAM Sample(INPUT, OUTPUT);

VAR

Vin: CHAR;

PROCEDURE Bump(VAR Vin: CHAR);

BEGIN {Bump}

IF Vin = ‘A’

THEN

Vin := ‘B’
END; {Bump}

BEGIN {Sample}

Vin := ’Q’;

Bump(Vin)

END. {Sample}

 

PROCEDURE Bump … END (s)

= s È {<†Bump†, †BEGIN IF Vin = ‘A’ THEN Vin := ‘B’ END†>}

 

т.е. простое определение даст

 

Bump(Vin) (s) = s(Bump) (s) = BEGIN IF Vin = ‘A’ THEN Vin := ‘B’ END (s)

 

что является корректным значением.

 

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

 

IF Vin = ‘A’ THEN Vin := ‘B’ = (Vin = ‘A’ -> Vin := ‘B’) | ( )

 

идентификатор Vin является параметром функции значения, в математическом смысле.

 

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

 

Bumo(DiffV)

 

где фактический параметр DiffV предположительно соответственно объявлен как CHAR, тогда значение могло быть получено вставкой DiffV везде в тексте Bump, где встречаетcя Vin. Таким образом,

 

Bumo(DiffV) = BEGIN IF DiffV = ‘A’ THEN DiffV := ‘B’ END

 

В общем случае, если P – идентификатор процедуры, и его объявление имеет формальный параметр X, тогда для фактического параметра Y, определим

 

P (Y) (s) = s (P) X<-Y (s)

 

где X <- Y означает, что внутри s(P) каждое вхождение X заменяется на Y. Обобщение до более чем одного параметра очевидно: идентификатор каждого фактического параметра заменяет соответствующий идентификатор формального параметра.

 

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

 

Bump(DiffV) = (Vin = ‘A’ -> Vin := ‘B’) Vin <-DiffV | ( )

= (DiffV = ‘A’ -> DiffV := ‘B’) | ( )

 

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

 

PROGRAM Alias(INPUT, OUTPUT);

VAR

One, Two : CHAR;

PROCEDURE SwitchThese2(VAR V1, V2, Temp: CHAR);

{ V1, V2, Temp := V2, V1, V1 }

BEGIN { SwitchThese2}

Temp := V1;

V1 := V2;

V2 := Temp

END; {SwitchThese2}

BEGIN {Alias }

SwitchThese2(One, Two, Two);

END. {Alias}

 

имеет

 

SwitchThese2(One, Two, Two) = Two := One; One := Two, Two := Two; = (Two := One)

 

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

(Two, Two, One) := (Two, One := One, Two)

Сложность с именами параметров называется «Алиасинг», потому что один фактический идентификатор имеет два формальных имени, в пример Two имеет два различных имени V2 и Temp.

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

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

 

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

 

могли бы быть полезными.

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

 

Процедуры с объявлениями.

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

 

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

 

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

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

 

PROCEDURE SwitchThese2(VAR V1, V2, temp: CHAR);

BEGIN

Temp := V1;

V1 := V2;

V2 := Temp

END

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

 

SwitchThese2(One, Two, Ch)

обменяет значения One и Two и также поместит в Ch старое значение One.

Возможность объявления локальных переменных в CF Pascal позволяет таким переменным как Temp быть скрытыми внутри процедуры. Например:

 

PROCEDURE Exchange(VAR Ch1, Ch2: CHAR);

{Обменивает Ch1 и Ch2}

VAR

Temp: CHAR;

BEGIN

Temp := Ch1;

Ch1 := Ch2;

Ch2 := Temp

END

 

Как и формальные параметры Ch1 и Ch2, локальная переменная Temp не известна и не может быть использована извне процедуры Exchange. Оператор процедуры

 

Exchange(One, Two)

 

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

 

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

Таблицы выполнения показывают поведение переменных в процедурах также, как они делали это для программ. Например, программа

 

PROGRAM Copy2(INPUT, OUTPUT);

{Копирует первые два символа из INPUT в OUTPUT}

PROCEDURE CopyChar(VAR F1, F2: TEXT);

{Копирует в F2 следующий символ их F1}

VAR

Ch: CHAR;

BEGIN {CopyChar}

IF NOT EOF(F1)

THEN

BEGIN

READ(F1, Ch);

WRITE(F2, Ch)

END

END; {CopyChar}

BEGIN {Copy2}

CopyChar(INPUT, OUTPUT);

CopyChar(INPUT, OUTPUT);

WRITELN

END. {Copy2}

 

 

имеет следующую таблицу выполнения.

 

 

  INPUT OUTPUT Ch EOF
  PROGRAM Copy2(INPUT, OUTPUT); BEGIN {Copy2} CopyChar (INPUT, OUTPUT) VAR Ch: CHAR; BEGIN {CopyChar} IF NOT EOF(INPUT) READ(INPUT, Ch); WRITE(OUTPUT, Ch) END; { CopyChar} CopyChar (INPUT, OUTPUT)ж VAR Ch: CHAR; BEGIN {CopyChar} IF NOT EOF(INPUT) READ(INPUT, Ch); WRITE(OUTPUT, Ch) END; { CopyChar} WRITELN END. {Copy2} ABC ABC/   ABC/     ABC/   ABC   _     A_     AB_   AB/_ AB     ?     A   §   ?     B   §     FALSE     FALSE     FALSE  

 

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

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

 

 








Не нашли, что искали? Воспользуйтесь поиском по сайту:



©2015 - 2024 stydopedia.ru Все материалы защищены законодательством РФ.