Н. Вирт - Программирование на языке Модула-2 (1160777), страница 11
Текст из файла (страница 11)
Параметры-переменные могут служить для передачи из процедуры вычисленногозначения.2. Формальный параметр подменяет подставляемый Фактический параметр.3. Фактический параметр не может быть выражением, а следовательно, и константой, дажеесли соответствующему Формальному параметру и не присваивается значение.4. Если Фактический параметр содержит индексы, то их вычисление происходит в моментподстановки Фактических параметров при вызове процедуры.5.
Типы Формального и Фактического параметров должны совпадать.12.2. Параметры-значенияПараметры-значения служат для передачи величин из вызывающей среды в процедуру, иони используются в преобладающем большинстве случаев. Соответствующий Фактическийпараметр -выражение, частным (и простейшим) случаем которого являются переменная иликонстанта. Формальный параметр-значение можно рассматривать как локальную переменнуюуказанного типа. При вызове вычисляется Фактическое выражение и результат присваиваетсялокальной переменной.
Отсюда следует, что Формальному параметру далее может бытьприсвоено новое значение, причем это никак не повлияет на операнды выражения. В известномсмысле можно считать, что Фактическое выражение и Формальный параметр после входа впроцедуру разъединяются. В качестве иллюстрации запишем приведенную ранее программувозведения в степень как процедуру.PROCEDURE Степень(VAR z: REAL; x: REAL; i: CARDINAL);BEGIN z := 1.0;WHILE 1 > 0 DOIF ODD(i) THEN z : = z*x END;x := x*x; i := i DIV 2ENDEND СтепеньВот примеры ее допустимых вызовов:Степень(u,2.5,3) Степень(А[i],В[i],2)При работе с параметрами-значениями следует иметь в виду, что, поскольку Формальныйпараметр представляет локальную переменную, для нее требуется память.
Это может оказатьсясущественным, если тип параметра является массивом из многих элементов. В этом случае52рекомендуется задавать параметр-переменную, даже если этот параметр используется только дляпередачи значения внутрь процедуры.Обратите внимание на то, что в приведенном примере процедуры параметры z и хобъявлены в различных секциях Формальных параметров, поскольку запись "VAR z,x: REAL"отнесла бы х тоже к параметрам-переменным, сделав невозможным подстановку вместо неговыражения общего вида.12.3.
Гибкие массивы-параметрыЕсли тип Формального параметра является массивом, то соответствующий Фактическийпараметр должен иметь тот же тип. Имеется в виду, что Фактический массив должен иметьэлементы того же типа и совпадающие границы диапазона индексов.
Часто это ограничениеоказывается слишком серьезным и поэтому желательна большая гибкость. Это обеспечиваетсяпосредством так называемого гибкого массива, который требует только, чтобы типы элементовФормального и Фактического массивов совпадали, и оставляет свободным диапазон индекса. Вэтом случае в качестве Фактических параметров могут подставляться массивы любого размера (слюбым числом элементов). Гибкий массив задается типом элемента, которому предшествует"ARRAY OF".
Например, процедура, описанная какPROCEDURE P(s: ARRAY OF CHAR)разрешает вызовы с массивами символов, имеющими любые границы индексов. Нижняяграница диапазона индексов в случае гибкого Формального массива равна нулю. Верхняя границаполучается посредством вызова стандартной Функции HIGH(s). Ее величина равна числуэлементов минус 1. Отсюда следует, что если массив, описанный какa: ARRAY [m..n] OF CHARподставляется вместо гибкого массива s, то s[i] обозначает а[m+i] для i = 0...HIGH(s), гдеHIGH(s) = n-m.13. ПРОЦЕДУРЫ-ФУНКЦИИМы познакомились с двумя возможностями передачи результата из тела процедуры вместо вызова: результат присваивается или нелокальной переменной, или параметру-переменной.Существует и третий метод: процедура-функция. Она позволяет использовать вычисленныйрезультат (как непосредственное значение) в выражении.
Идентификатор процедуры-Функцииобозначает как вычисление, так и вычисленный результат. Описание процедуры-Функциихарактеризуется указанием типа результата после списка параметров. В качестве примерапреобразуем ранее приведенную процедуру вычисления степени в процедуру-Функцию.PROCEDURE степень(х: REAL; i: CARDINAL): REAL;VAR z: REAL;BEGIN z := 1.0;WHILE i > 0 DOIF ODD(i) THEN z := z*x END;x := x*x; i:= i DIV 2END;RETURN zEND степень53Возможные вызовы таковы:u := степень(2.5,3)А[i] := степень(В[i],2)u := х + степень(у,i+1)/степень(z,i-1)Оператор, передающий результат, состоит из ключевого слова RETURN, за которымследует выражение, вычисляющее результат. Оператор возврата может встретиться в несколькихместах в теле процедуры: он прекращает выполнение тела и осуществляет возврат в место вызова.Обычно, однако, оператор возврата помешается непосредственно перед завершающим ENDпроцедуры.
Операторы возврата могут также использоваться в обычных процедурах, но в этомслучав за словом RETURN не следует выражение. Это средство может служить сигналомаварийного завершения. Подразумевается, что оператор возврата неявно помешается в концекаждой процедуры.$ОператорВозврата = "RETURN" [Выражение].Вызов внутри выражения называется обозначением функции. Его синтаксис такой же, каки у вызова процедуры. Однако список параметров обязателен, хотя он может быть и пустым.Обратимся теперь еше раз к предыдущему примеру суммирования элементов массива и запишемего как процедуру-функцию.PROCEDURE cyммa(VAR а: Вектор; n: CARDINAL): REAL;VAR i: CARDINAL; s: REAL;BEGIN s := 0.0;FOR i := 0 TO n-1 DOs := a[i] + sEND;RETURN sEND суммаЭта процедура в том виде, как она записана, суммирует элементы а[0]..
.а[n-1], где n заданокак параметр-значение и может отличаться от числа элементов N (но не превосходить его!). Вболее изящном решении а описывается как гибкий массив, поэтому явное указание размерамассива отсутствует.PROCEDURE cуммa(VAR x: ARRAY OF REAL): REAL;VAR i: CARDINAL; s: REAL;BEGIN s := 0.0;FOR i := 0 TO HIGH(x) DOs := x[i] + sEND;RETURN sEND сумма54Очевидно, что процедура может выдавать более одного результата, осуществляяприсваивания различным переменным.
Однако лишь одна величина может быть возвращена какрезультат функции. Более того, ее тип не может быть структурированным. Следовательно, другиерезультаты должны передаваться в место вызова через параметры вида VAR или черезпеременные, нелокальные в процедуре-Функции. Рассмотрим для примера процедуру,вычисляющую основной результат, определенный как значение функции, и побочный,используемый для подсчета числа вызовов процедуры.PROCEDURE квадрат(х: CARDINAL): CARDINAL;BEGIN n := n + 1;RETURN x*xEND квадратВ этом примере нет ничего примечательного пока побочный результат используется попрямому назначению (указанному выше).
Возможно, однако, и ошибочное использование:m := квадрат(m) + nЗдесь побочный результат входит как операнд в выражение, содержащее обозначениесамой функции. Следствием этого является, например, тот Факт, что значенияквадрат(m) + n и n + квадрат(m)различаются, явно бросая вызов основному закону коммутативности сложения.Присваивания значений из процедур-Функций нелокальным переменным называютсяпобочными эффектами. Программисту следует отчетливо осознавать их способность приводить кнеожиданным результатам в случае их неосторожного использования.Подведем итог сказанному:1. Процедура-Функция определяет результат, который используется в месте вызова какоперанд выражения.2.
Результат процедуры-Функции не может быть структурированным.3. Если процедура-Функция выдает вспомогательные результаты, то говорят, что она имеетпобочный эффект. Использование таких процедур требует особой аккуратности. Если процедурафункция передает результаты посредством параметров-переменных, то желательно вместо нееиспользовать обычную процедуру.4. Рекомендуем выбирать в качестве идентификаторов функций имена существительные.Существительное в этом случае обозначает результат Функции.
Булевым Функциям вполнеподходят прилагательные. Обычную же процедуру следует обозначать глаголом, описывающим еедействие.14. РЕКУРСИЯПроцедуры могут не только вызываться откуда-то, но и сами себя вызывать. Так каквызвать можно любую видимую процедуру, то процедура может вызвать и себя. Такаясамоактивация называется рекурсией. Ее использование уместно, когда алгоритм определенрекурсивно и, в особенности, когда он применяется к рекурсивно определенной структуре данных.Рассмотрим в качестве примера задачу печати всех возможных перестановок n различныхобъектов.
Назвав эту операцию Переставить(n), мы можем записать ее алгоритм следующимобразом:55Сначала оставить а[n] на своем месте и сгенерировать все перестановкиа[1]...а[n-1], вызвав процедуру 63объектовПереставить(n-1), затем повторить процесс, поменяв а[n] местами с а[i] при i = 1,повторить это для всех значений i = 2...n-1. Этот рецепт записывается в виде программыследующим образом (в качестве переставляемых объектов используются литеры):MODULE Перестановка:FROM InOut IMPORT Read,Write,WriteLn;VAR n: CARDINAL; ch: CHAR;a: ARRAY [1..20] OF CHAR;PROCEDURE Вывод;VAR i: CARDINAL;BEGINFOR i := 1 TO n DO Write(a[i]) END;WriteLnEND Вывод;PROCEDURE Переставить(k: CARDINAL);VAR i: CARDINAL; t: CHAR;BEGIN IF k = 1 THEN ВыводELSE Переставить(k-1);FOR i := 1 TO k-1 DOt := а[i]; a[i] := a[k]; a[k] := t;Переставить(k-1);t := a[i]; a[i] := a[k]; a[k] := t;ENDENDEND Переставить;BEGIN Write(">"); n := 0; Read(ch);WHILE ch > " " DOn := n + 1; a[n] := ch; Write(ch); Read(ch)56END;WriteLn; Переставить(n)END Перестановка.Результаты, полученные в случае использования трех литер, таковы:ABC ВАС СВА ВСА АСВ CABКаждая цепочка рекурсивных вызовов должна на каком-то шаге завершиться, и,следовательно, любая рекурсивная процедура должна содержать рекурсивный вызов внутриусловного оператора.