В.Г. Абрамов, Н.П. Трифонов, Г.Н. Трифонова - Введение в язык Паскаль (1107618), страница 37
Текст из файла (страница 37)
Первыйформальный параметр А объявлен параметром-значением. Следовательно,по общему правилу использования соответствующих фактических параметров, при входе в процедуру порождается внутренняя переменная А типаВект и этой переменной присваивается значение фактического параметра переменной X. Но так как значением этой переменной является не отдельное данное, а упорядоченная последовательность компонент, образующихэто значение, то все эти компоненты должны быть скопированы для формирования значения переменной А. В итоге для рассматриваемого операторапроцедуры получается следующий эквивалентный ему блок:var j: integer; А: Вект;for j:=1 to N do AC j 3:=X С j 3;begin u:=AC13;•for i : = 1 to N doif А С i 3 >u then u:=ACi3endТаким образом, переменную-массив можно вызывать значением, однакоследует иметь в виду, что на копирование значения такой переменной привходе в процедуру тратится довольно много времени, а для размещениявнутренней для процедуры переменной, соответствующей формальномупараметру, приходится отводить дополнительное место в памяти.
Поэтомувызова по значению переменных-массивов (и вообще переменных производных типов) без особой на то необходимости следует избегать. В частности, в рассматриваемом примере лучше было бы дать описание процедуры с таким заголовком (тело процедуры можно сохранить прежним):procedure MAXAR1(var А: Вект; var s: real);В таком случае выполнение оператора процедуры MAXAR1(X, и) сведется к выполнению следующего эквивалентного ему блока:begin ut =ХС 1 3 ;for i:=1 to N doif X С i 3 >u then u:=XCi3endОчевидно, что такой блок будет выполняться значительно быстрее и расходпамяти будет меньше.В связи с этим может возникнуть вопрос — а бывают ли вообще случаи,когда переменную-массив целесообразно вызывать именно значением,а не по имени? Да, такие случаи бывают.Пусть, например, при решении некоторой задачи по имеющимся целочисленным векторам а(а0, а , , ....
и 1 0 ) , b(b0, bly ..., bl0) и целочисленным s, t152надо вычислить и = P t 0 (х + t).v- Qi0(s ~ t ) ,Рп(х) = р0хп+ р1х"~1n 1Qn(x) = q0x" +q1x гдеPi =+...+рп-1х+рп,+ ... + q„-ix+q„IЯt=bj - 1 при bj > 0,bt + 1 при bj < 0Очевидно, что здесь удобно ввести в употребление процедуру, котораявычисляет значение полинома по задаваемому значению аргумента и вектору, из которого получаются коэффициенты этого полинома.Обратим внимание на то, что по постановке задачи векторы а и b изменяться не должны!Очевидно, что в процедуре удобно иметь три параметра: аргумент полинома, вектор, из которого получаются коэффициенты полинома, и переменная-результат.
Первый из этих параметров удобно вызывать значением,а третий необходимо вызывать по имени, поскольку результат надо передать в основную программу. Что касается второго параметра, то его, вообще говоря, можно вызывать как по имени, так и значением. Рассмотримоба эти случая.Предварительно заметим, что для вычисления полинома удобно воспользоваться схемой Горнера:Рп(х)= (...((о+р0) *х +Pi)*x + . .
. + / ? „ + р „ ,а для этого необходимо предварительно иметь коэффициенты полиномав виде компонент некоторого вектора.Итак, пусть в программе имеются описанияconst п=10;type массив=аггау CO..n3 of integer;var s,t,u,v,i:integer;А,В: массив;Дадим описание нужной нам процедуры, в которой задаваемый целочисленный вектор является параметром-переменной. Заголовок этой процедуры может иметь вид:procedure POL<x: integer; var г: массив; var с: integer);Приводимое ниже тело этой процедурыbegin for i:=0 to n doif г Сi 3 >0 then rCi3:=rCi3-l else rCi3:=rСi3 + 1;с : =0for i:=0 to n doс:=с*к+г[i 3endбыло бы неправильным, поскольку при выполнении такой Процедурыизменялся бы вектор, задаваемый в качестве второго фактического параметра, в то время как он изменяться не должен.
Поэтому в теле про153цедуры придется ввести в употребление вспомогательный вектор (дадимему имя d ) . Сначала в качестве компонент этого вектора примем компоненты задаваемого при обращении к процедуре вектора, а затем преобразуем его в вектор, компонентами которого являются коэффициентыполинома:procedure POL<>s: integer; var rs массив; var с: integer);var d: массив;begin -for i : =0 to n do d С i Э : =r С i J ;•for i : =0 to n doif dСi 3 >0 then dСiJ:=dСiЭ-1 else dCi]:=dСi3 + 1;c:=0;for i:=0 to n do с:=c*x+dCi3endОбращения к этой процедуре будут иметь вид P0L(s +1, А, и),P0L(s - t, В, v).Чтобы массив, задаваемый в качестве фактического параметра, при выполнении процедуры не подвергался изменениям, нам пришлось в теле процедуры изготовить с него копию в виде вспомогательного массива d, с которым уже и работала процедура.
Чтобы снять с программиста заботупо изготовлению такой копии, как раз и удобно второй параметр процедуры сделать параметром-значением, а не параметром-переменной:procedure POLIIM0M(>:: integer; г: массив; var c.s integer);begin for i:=0 to n doif г С i]>0 then г С i 3 : =r С i 3-i else rCi3:=rСi3 + 1;c:=0;for i:=0 to n do c:=c*x+rCi3endПоскольку г является Параметром-значением, то при выполнении, например, оператора процедуры P0LIN0M(s + t.
А, и) при входе в процедурус фактического параметра (переменной-массива А) будет изготовленакопия в виде значения внутренней для процедуры переменной г, и вседействия, предусмотренные в теле процедуры над параметром г, будутпроизводиться над значением именно этой внутренней переменной, а не переменной А, которая вообще недоступна из тела процедуры. Вот в такихслучаях, когда значение переменной, являющейся фактическим параметром, должно остаться без изменения, а в теле процедуры приходится изменять это значение, как раз и удобно использовать параметр-значение.Аналогично обстоит дело и с переменными других производных типов(например комбинированного типа), используемыми в качестве фактических параметров процедур.8.3.
Синтаксис процедурПосле того, к а к мы познакомились с понятием процедуры и рассмотрелипримеры описаний процедур и их использования с помощью операторовпроцедур, дадим более точные определения этих понятий в паскале.1548.3.1. Синтаксис описания процедурыОбщее определение описания процедуры было уже дано — напомним этоопределение:< описание процедуры >:: = < заголовок процедуры >; < блок >При этом блок (тело процедуры) синтаксически определяется точнотак же, как и блок в паскаль-программе. Поэтому необходимо уточнитьлишь определение заголовка процедуры:(заголовок процедуры):: = procedure <имя процедуры) |procedure <имя процедуры) (( список формальных параметров))Имя процедуры есть идентификатор, а список формальных параметровопределяется следующим образом:< список формальных параметров):: = < секция формальныхпараметров>{ ;<секция формальных параметров)}.Секцию формальных параметров определим пока в предположении, чтопараметрами процедуры могут быть значения и переменные.
По завершении рассмотрения процедур и функций мы уточним определение этогопонятия:(секция формальных параметров):: = <имя) {, <имя)}: <имя типа) |var ( и м я ) {, (имя)}: (имя типа)}где ( и м я ) — идентификатор, используемый в качестве фактического параметра.Дадим некоторые пояснения к приведенным определениям.Как видно, список формальных параметров (если он присутствует)состоит из одной секции или из нескольких секций, отделенных друг отдруга точкой с запятой.
В каждой секции может присутствовать один илинесколько формальных параметров — в последнем случае они отделяютсядруг от друга запятой. Число формальных параметров, включаемых в однусекцию, определяется программистом — пока будем исходить из того, чтов секцию входит один параметр, который обозначим через р.Отсутствие служебного слова в начале секции означает, что входящийв эту секцию параметр р представляет в теле процедуры некоторое значение. Служебное слово var в начале секции означает, что этот параметр рпредставляет некоторую переменную, введенную в употребление (существующую) вне данной процедуры. С параметрами-значениями и параметрами-переменными мы уже знакомы.
На самом деле параметрами процедурмогут быть также процедуры и функции — эти два случая мы пока рассматривать не будем.После параметра р через двоеточие указывается имя типа значения (переменной), которое (которая) представляется этим параметром р. Например, заголовок процедуры может иметь вид:procedure P<a;char; b:char; var c:real; var d:real; e:char)Здесь список формальных параметров содержит пять секций; параметрыa, b, е представляют значения типа char, а параметры с, d — переменныетипа real.
При этом каждая секция содержит только один параметр.155Для достижения большей компактности записи, соседние параметры,представляющие либо значения, либо переменные одного и того же типа,можно объединить в одну секцию, чтобы многократно не повторять имятипа и служебное слово var. Поэтому приведенный выше заголовок процедуры можно записать короче, без изменения его смысла и правил пользования данной процедурой:p r o c e d u r e Р(а,Ь: char; v a r c,d: real; e: char)Таким образом, секция — это перечень однотипных параметров-значений или параметров-переменных. Следует подчеркнуть, что служебноеслово и и м я типа относятся только к данной секции, причем ко всем включенным в эту секцию параметрам, так что, например, заголовокp r o c e d u r e P P ( v a r х: real; у: real)не эквивалентен заголовкуp r o c e d u r e P P ( v a r х,у: real)который является допустимым сокращением заголовкаp r o c e d u r e PPCvar х: real;var у: real).Итак, основные вопросы синтаксиса и семантики описания процедурымы рассмотрели.
Теперь дадим некоторые дополнительные пояснения.1. Какое имя давать описываемой процедуре? Какое угодно, лишь быэто имя в данной программе не совпадало с именами других программныхобъектов (констант, переменных, процедур и т.д.) и с именами типов,используемых в программе. Как обычно, имя процедуры желательно выбирать таким, чтобы оно отражало назначение процедуры (ИНТЕГРАЛ,МАХЗ, УМНМАТР и т.д.) .2. Сколько параметров должно быть у процедуры? Это зависит от назначения процедуры, и этим определяется гибкость ее использования. Как мывидели, процедура может вообще не иметь параметров.
В рассмотренномранее примере подобного рода процедура МАХ2А всегда применяласьк значениям переменных а и Ь, а большее из этих значений всегда присваивалось переменной s. Как мы видели, использование такой процедурывлекло за собой необходимость вводить в употребление дополнительныепеременные и использовать дополнительные операторы присваиванияв программе. Наличие у процедуры параметров обеспечивает гибкостьиспользования процедуры и удобство ее употребления в различных ситуациях. Однако в этом случае при каждом обращении к процедуре ее приходится настраивать на заданные фактические параметры, что снижает"быстродействие" процедуры, т.е. ведет к увеличению затрат машинноговремени на ее выполнение. Так что количество параметров у процедурызависит и от специфики решаемой задачи, и от того, к а к и м и характеристик а м и программист хочет наделить процедуру.3.