1611678431-0e68e83522cb9d960ac896aa5d90854d (826635), страница 13
Текст из файла (страница 13)
Спецификацию выходныхформальных параметров определяет синтаксическая диаграмма рис. 6.4.OutFPSection = var ident { "," ident } ":" FormalType.Рис. 6.4. Спецификация выходных формальных параметровПри вызове процедуры в фактических параметрах вычисляются все индексныевыражения, если они есть, а полученные переменные связываются ссоответствующими выходными формальными параметрами. Эта связь имеетместо до тех пор, пока не завершится выполнение данного вызова, и любыедействия с формальными параметрами-переменными, выполняемые в это времявнутри процедуры, в действительности выполняются над соответствующимипеременными, подставляемыми в качестве фактических параметров (вчастности, если фактический параметр не содержит индексов, то он и есть тапеременная, с которой связывается соответствующий формальный параметр).Например, в программе МаксТри, приведенной ниже, при первом вызовепроцедуры Зам обмениваются своими значениями переменныеи , а привтором -и .module МаксТри;(* Максимум из трех целых *)var X,Y,Z : integer;procedure Зам(var A : Integer; var B : Integer);var C : integer;begin C := A; A := B; B := C end Зам;beginread(X,Y,Z);if XY then Зам(X,Y) end; (**)if XZ then Зам(X,Z) end; (*write(X)end МаксТри.*)Рассмотрим вызов Зам(K, M[K]), в котором-- это целая переменная, а-переменная типа array[1..100] of integer.
Поскольку индексные выражения вфактических параметрах вычисляются до связывания параметров, справедливосвойствоЗамВыходные параметры -- важное и мощное средство программирования, однаковозможность доступа к одной и той же переменной через разные имена,возникающая при подстановке выходных параметров, таит в себе определеннуюопасность. Нужно тщательно следить за тем, чтобы при вызовах два разныхформальных параметра не были связаны с одной и той же переменной или еекомпонентами. Опасности, которые могут проявиться при нарушении этогоправила, показаны ниже, на примере процедуры УМН для умножения двухматриц.type MATР = array 2 , 2 of integer;procedure УМН (var X, Y, Z : MATР);beginZ[1,1] := X[1,1] Y[1,1] + X[1,2] Y[2,1];Z[1,2] := X[1,1] Y[1,2] + X[1,2] Y[2,2];Z[2,1] := X[2,1] Y[1,1] + X[2,2] Y[2,1];Z[2,2] := X[2,1] Y[1,2] + X[2,2] Y[2,2];end;Пусть даны переменныеитипа MATР и матрицыТогда справедливы следующие утвержденияУМНУМНУМНС другой стороны, если у процедуры УМН два первых параметра сделатьпараметрами значениями, т.е.
изменить заголовок наprocedure УМН (X, Y: МАТР; var Z : MATР);получаем следующие свойства:УМН(A,B,C)УМН(A,B,A)УМН(A,B,B)28. Блоки и правила локализации имен. Локальные, глобальные и стандартные имена.Указательные и динамические переменные6.1.2 Блоки и локализация именКаждая простая программа на языке Zonnon состоит из заголовка программы(заголовка модуля) и ее тела. Тело программы -- это блок с точкой в конце. Блокзадается синтаксической диаграммой рис. 6.1.Block = Declarations BlockStatement SimpleName.Declarations = { SimpleDeclaration } { ProcedureDeclaration }.SimpleDeclaration = ( const { ConstantDeclaration ";" }| type { TypeDeclaration ";"}| var { VariableDeclaration ";" }ProcedureDeclaration = ProcedureHeading ";" Block ";".ProcedureHeading = procedure ProcedureName [ FormalParameters ].FormalParameters = "(" [ FPSection { ";" FPSection } ] ")" [ ":" FormalType ].Рис.
6.1. Синтаксис блокаВ теле программы используются объекты (константы, типы, переменные,процедуры и функции) двух сортов: локальные (их имена определены всоответствующих разделах блока) и нелокальные объекты. Нелокальныеобъекты программы -- это так называемые стандартные объекты,определенные в самом языке Zonnon и образующие контекст, в который"погружается" каждая Zonnon-программа. Для локальных объектов программысущественно то, что их имена связаны блоком: идентификаторы объектовимеют смысл только внутри блока, причем выбор того или другого конкретногоидентификатора в качестве имени объекта не влияет на смысл этого объекта.Можно сказать, что определения локальных объектов "не видны" извне блока, вкотором они находятся. Блок ограничивает и областьсуществования локального объекта: в частности, после окончания процессавыполнения, описанного программой, пространство в памяти ЭВМ, отведенноетранслятором в момент начала исполнения блока под его локальныепеременные, становится снова свободным, и транслятор может его использоватьпроизвольным образом.Из синтаксической диаграммы видно, что каждое описание подпрограммызавершается точкой с запятой и состоит из заголовка и блока, называемоготелом подпрограммы.
Подпрограмма -- это не просто способ сокращения текста,но и, что более важно, средство абстракции, разложения программы налогически связанные замкнутые компоненты, определяющие ее структуру.Подпрограмма может иметь свои собственные локальные объекты, именакоторых вне ее тела недоступны и которые существуют только во времяисполнения подпрограммы. Например, программу СуммаДвухЧисел можно (инужно с точки зрения любого разумного критерия качества) переписатьследующим образом:module СуммаДвухЧисел;var Число1, Число2, Сумма : real;procedure Линия;const N = 100;var I : integer;beginfor I := 1 to N do write('-') end;writelnend Линия;beginread (Число1, Число2); Сумма := Число1 + Число2; Линия; Линия;writeLn(Число1); writeln(Число2); Линия; writeln(Сумма): Линия; Линияend СуммаДвухЧисел.Поскольку подпрограмма описывается как локальная по отношению кпрограмме или другой подпрограмме, блоки в программе могут быть вложеныодин в другой.
Блоки программы образуют иерархию по вложенности: любыедва блока либо не пересекаются, либо один из них целиком содержится вдругом. Поэтому для любого блокасуществует единственнаяпоследовательность объемлющих его блоков,блок всей программы, а каждый блокв которой--является телом подпрограммы,описанной в блокеКак и в теле программы, в теле любой подпрограммы можно использовать нетолько локальные объекты, но и нелокальные (глобальные по отношению кблоку), т.е. объекты, определенные вне данной подпрограммы и образующиеконтекст, содержащий описание подпрограммы.
Этот контекст образуется изстандартных объектов и объектов, определенных в блоках, в которые вложенданный. Важно понимать различие между существованием объектов контекстаи их доступностью. "Не видны" (не доступны через имена) из блока те объектыконтекста (точнее, их определения), которые "экранируются" другимиописаниями. В частности, не видны все те объекты контекста, имена которыхсовпадают с именами локальных объектов данного блока. Другимисловами,область действия описания (та часть текста программы, в котором этоопределение доступно) не распространяется на блок, вложенный в данный, еслив нем есть описание объекта с тем же именем.
Таким образом, каждый блоквводит новый уровень обозначений, и имена локальных объектов можновыбирать вне зависимости от контекста, в который "погружен" блок.Рассмотрим процесс выполнения на ВМ программы, содержащейподпрограммы. Подпрограмма -- это программно реализованная операция ВМ.После того, как подпрограмма (реализация операции) описана в программе, этаоперация становится равноправной операцией ВМ и может быть выполнена ввыражении (случай функции) или в операторе (случай процедуры).
Однакоисполнение такой операции (активация подпрограммы при ее вызове)осуществляется на своем экземпляре ВМ, создаваемом под каждый вызовподпрограммы. Каждый такой экземпляр ВМ имеет свою собственную памятьдля локальных объектов подпрограммы и может напрямую оперировать сэлементами других экземпляров ВМ, связанных с объектами контекста, вкоторый "погружена" данная подпрограмма. При активации подпрограммысначала создается новый экземпляр ВМ для выполнения данного вызоваподпрограммы, а затем приостанавливается выполнение ВМ, содержащейвызов, и начинается исполнение нового экземпляра ВМ. Когда новый экземплярВМ завершит отработку данной активации подпрограммы и перестанетсуществовать, возобновляется выполнение вызвавшего его экземпляра ВМ с тойоперации, которая следует за операцией активации.29.
Рекурсия, рекурсивные процедуры и функции, поколения переменных, сравнениеитеративного и рекурсивного представлений алгоритмов6.1.7 Рекурсивные подпрограммыНекоторый объект называется рекурсивным, если он содержит сам себя илиопределен с помощью самого себя. Рекурсия является мощным средством вопределениях; и мы уже использовали ее во внешних спецификациях, а такжепри задании синтаксиса языка Паскаль (например, рекурсивно определяютсятакие понятия, как выражение или оператор). Мощность рекурсии связана стем, что она позволяет задать бесконечное множество объектов с помощьюопределения конечного "размера". Очень важно, что язык Zonnon допускаетиспользование рекурсии при описании подпрограмм.
Например, любой цикл сусловием на продолжениеwhile B do S endможет быть реализован вызовом следующей процедуры, содержащей вызовсамой себя:procedure ЦИКЛ; begin if B then S; ЦИКЛ end end ЦИКЛ.Скажем, что некоторая подпрограмма P содержит обращение к подпрограммеQ, если в тексте Р содержится явное обращение к Q или к некоторой такойподпрограмме R, что R содержит обращение к подпрограмме Q (заметим, чтоприведенное определение рекурсивно!).
Подпрограмма называется рекурсивной,если она содержит обращение к самой себе. Таким образом, использованиерекурсии не всегда сразу видно из текста программы.С подпрограммой связано множество локальных объектов, новые экземплярыкоторых создаются каждый раз, когда подпрограмма вызывается. Например,следующая Zonnon-программа осуществляет обращение любого непустогослова (последовательности символов), находящегося во входном файле:module СЛОВО;procedure ОБРАЩЕНИЕ;var A : Char;begin if ~Eof then read(A); ОБРАЩЕНИЕ; write(A) end end ОБРАЩЕНИЕ;begin ОБРАЩЕНИЕ end СЛОВО.Хотя при исполнении программы СЛОВО сосуществует несколько переменныхс именем A (в момент начала печати результата имеется больше экземпляровпеременной A, чем длина входного слова), не возникает никаких конфликтовпри использовании имен: идентификатор A всегда ссылается на локальнуюпеременную с именем A, созданную последней, т.е. на тот экземплярпеременной A, который был создан при последнем обращении к процедуреОБРАЩЕНИЕ.