В.Г. Абрамов, Н.П. Трифонов, Г.Н. Трифонова - Введение в язык Паскаль (1107618), страница 44
Текст из файла (страница 44)
Чтобы подчеркнуть, что это другаяпеременная, обозначим ее через n l . Этой переменной присваивается значение фактического параметра, так что nl = 2, и поскольку nl Ф 0, то выполнение условного оператора в теле процедуры сводится к выполнениюоператораfact:=2*fact(1)При его выполнении для вычисления fact (1) снова производится обращение к процедуре fact, вводится в употребление новая локальная переменная п2, которой присваивается значение n2 = 1, и поскольку п2 Ф 0, выполнение тела процедуры сводится к выполнению оператораfact:=l*fact(О)Заметим, что при этом каждый раз откладывалось завершение вычисления выражения в правой части оператора присваивания. При очередном обращении к процедуре-функции получим пЗ = 0 и выполнение тела процедуры сведется к выполнению оператораfact:=1По получении нами этого значения завершается выполнение оператораfact := 1 * fact(0), что дает значение f a c t ( 1 ) = 1; далее завершается выполнение оператора fact := 2 * fact(l), что дает значение fact(2) = 2, и, наконец,завершается вычисление fact := 3 * fact (2), что и дает искомый результатfact(3) = 3 * 2 = 6.Любое рекурсивное определение функции можно заменить и нерекурсивным, с использованием оператора цикла.
Например, функцию* п\ можноописать и так:function fact(n: натур): натур;var k,i: натур;begin к:=1;for i:=l to n do k:=k*i;fact:=kend;Как видно, рекурсивность — это не свойство самой функции, а свойствоее описания. В связи с этим возникает вопрос — а какое описание (рекурсивное или нерекурсивное) лучше? В общем случае ответ таков: рекурсивное описание обычно короче и нагляднее, а нерекурсивное — длиннее.
Затона вычисление рекурсивной функции затрачивается больше машинноговремени (за счет повторных обращений к процедуре-функции) и памятимашины (за счет дублирования локализованных в процедуре переменных).Поэтому при выборе способа описания функции следует решить, чемуотдать предпочтение — эффективности программы или ее компактности.1829.5. Параметры-функции и параметры-процедурыДо сих пор мы рассматривали только такие процедуры (операторы ифункции), параметрами которых являются значения и (или) переменные.Однако иногда бывает нужно, чтобы некоторыми параметрами были в своюочередь процедуры или функции.
Проиллюстрируем эту ситуацию на следующем примере.Пусть требуется вычислитьsin 10 + sin 11 + . . . + sin 50~ (1 + 1/2 + 1/3 + . . . + \/(п + 20)) 2при заданном значении п.Нетрудно заметить, что здесь при вычислении как числителя, так и знаменателя приходится решать по сути дела одну и ту же частичную задачу —суммирование значений некоторой функции, аргумент которой принимаетпоследовательные целочисленные значения из некоторого отрезка. Естественно, алгоритм решения этой частичной задачи хотелось бы оформить ввиде процедуры (дадим ей имя СУМ) - в данном случае процедуры-функции, поскольку каждая сумма является промежуточным результатомвычислений.Заметим, что сумму как в числителе, так и в знаменателе можно представить в видеf(m) + f(m + 1) + .
. . + f(k)где через т и к обозначены границы изменения аргумента, а через f —функция, используемая при суммировании. Эти идентификаторы m, k, f идолжны быть параметрами процедуры. При этом т и к должны быть параметрами-значениями, а формальный параметр f представляет имя некоторой функции, так что он должен быть параметром-функцией. В связи сэтим в паскале для процедур и предусматривается возможность использования параметров-процедур и параметров-функций.Здесь возникают два вопроса:а) что должно быть фактическим параметром для формального параметра рассматриваемого вида;б) как оформляется соответствующий формальный параметр в заголовке процедуры.Для ответа на первый вопрос заметим, что процедура СУМ должна нетолько суммировать значения функции, но и вычислять эти значения, включая и вычисление значений ее аргумента.
Поскольку закон изменения значений аргумента зафиксирован, а границы его изменения будут заданы спомощью соответствующих фактических параметров, то для процедурыосталось необходимым задать еще только имя суммируемой функции.Это имя должно быть именем уже описанной в программе функции.Используя это имя, заданное в качестве фактического параметра, процедура СУМ и будет обращаться к нужной процедуре-функции длявычисления очередного слагаемого суммы.Для ответа на второй вопрос надо учесть следующие обстоятельства,связанные как с удобством использования описьюаемой процедуры, так ис обеспечением надежности программы. Итак, прежде всего необходимоуказать, что данный формальный параметр представляет некоторую проце183'дуру или функцию. Кроме того, ясно, что в качестве фактического параметра можно задавать и м я не любой процедуры. Если, скажем, формальныйпараметр представляет процедуру с двумя параметрами, то в качествефактического параметра нельзя задавать процедуру с другим числом параметров; если формальный параметр представляет вещественную функцию,то фактическим параметром не может быть логическая функция и т.д.Поэтому для устранения возможности дополнительных ошибок хотелосьбы иметь возможность достаточно легко по заголовку описываемой функции определять, какие же процедуры допустимы в качестве фактическихпараметров для того или иного параметра-процедуры или функции.
Наличие такой информации в заголовке процедуры позволяло бы трансляторуосуществлять контроль за корректностью использования этой процедурыв программе, своевременно выявлять некорректные обращения к ней и темсамым способствовать повышению надежности программы.В связи с изложенными выше обстоятельствами в паскале формальный параметр-процедура (функция) задается в виде заголовка той "типичной" процедуры (функции), имя которой может быть задано в качестве фактического параметра — при этом имя такой "типичной" процедуры и ее формальные параметры выбираются достаточно произвольным образом.Внимание! С целью повышения надежности программ в паскале в качестве параметров нельзя использовать процедуры, фактические параметрыкоторых вызываются по имени, т.е. параметрами таких процедур могутбыть только параметры-значения.
Это ограничение несколько снижает гибкость использования процедур, но здесь язык явно отдает предпочтениевопросу надежности программ.Теперь вернемся к рассмотрению нашего примера. Итак, для описанияи использования интересующей нас процедуры СУМ предварительно должны быть определены те функции, которые будут использоваться в качествефактических параметров. Функция sin(x) является стандартной, поэтомуее можно не описывать в явном виде. Вторая функция есть 1/дс - такойстандартной функции в паскале нет, поэтому определим ее с помощьюсоответствующего описания функции (напомним, что если формальныйпараметр процедуры представляет вещественное значение, то в качествефактического параметра для него можно задавать целочисленное значение):• f u n c t i o n ОБРВЕЖх: real): r e a l ;b e g i n ОБР'ВЕ/1: =1 /х e n d ;Теперь процедуру-функцию СУМ можно ввести в употребление с помощью следующего описания:• f u n c t i o n CUM ( f u n c t i o n f ( x : real): real;m,k: integer): real;v a r i: integer; r: real;b e g i n r:=0;f o r i:=m t o kСУМ:=rend;184do r:=r+f(i);С использованием этой процедуры-функции вычисление значения t можно задать с помощью следующего оператора присваивания:t:«СУМ(sin,10,50)/sqr(СУМ(ОБРВЕЛ,1,n+20)*Что касается параметров-процедур, то все обстоит аналогичным образом.
Конечно, в описании одной и той же процедуры (оператора или функции) могут использоваться к а к параметры-процедуры, так и параметрыфункции.9.6. Процедуры и пошаговая детализацияДо сих пор мы рассматривали процедуры как средство обеспечения компактности и наглядности программ. Однако есть и еще одна важная сторона использования процедур: этот аппарат является эффективным средствомподдержки метода нисходящего проектирования (пошаговой детализации)программ, о котором говорилось в главе 5.Напомним, что суть этого метода состоит в последовательном выделениииз исходной задачи все более простых подзадач, а тем самым разработка(проектирование) алгоритма решения исходной задачи сводится к егокомпозиции из частичных алгоритмов, предназначенных для решениявыделенных подзадач.Сейчас нам важно обратить внимание на два обстоятельства, связанныес процедурами в паскале.Во-первых, язык не накладывает каких-либо ограничений на тот фрагмент программы, который может быть объявлен процедурой (за исключением требований синтаксического характера).
Следовательно, алгоритмрешения любой из выделенных из основной задачи подзадач может бытьпредставлен в виде описания подходящей процедуры (процедуры-оператора или процедуры-функции). Это обстоятельство позволяет записыватьраздел операторов исходной паскаль-программы в окончательном видеуже на самых ранних этапах разработки программы.Действительно, приняв решение выделить в процедуры некоторыечастичные алгоритмы, мы можем фактически еще не иметь самих этих алгоритмов.