В.Г. Абрамов, Н.П. Трифонов, Г.Н. Трифонова - Введение в язык Паскаль (1107618), страница 42
Текст из файла (страница 42)
Но ведь здесь речь идет не о том, чтобы вычислить значение функции, а об указании того, что' в качестве значения функциинадо принять текущее значение переменной к.2. Имя функции используется в правой части оператора присваиванияк а к имя переменной, например:function FACT<n:integer):var i:172integer;integer;begin FACT:= 1;•for i : =1 to n doFACT:=FACT*iendТакое описание функции FACT является неверным, потому что записьFACT * i не является арифметическим выражением.
В самом деле, вэтой записи FACT может быть либо переменной, либо вызовом функции без параметров. Однако согласно данному описанию идентификаторFACT есть имя функции, а не переменной. Вызовом функции записьFACT тоже быть не может, поскольку в вызове функции должно бытьуказано столько же фактических параметров, сколько формальных параметров содержится в заголовке функции при ее описании.При знакомстве с особенностями тела процедуры-функции может возникнуть вопрос — зачем предусматривается возможность наличия в этомтеле нескольких операторов присваивания, указывающих, какое значение следует принять в качестве значения определяемой функции? Можетли такая возможность оказаться действительно полезной? Да, такая возможность весьма полезна в тех случаях, когда искомое значение функцииприходится переопределять в процессе выполнения тела процёдуры-функции.
В качестве иллюстрации сказанному рассмотрим функцию, аргументами которой являются строка (5) и литерная переменная (?); функция должна принять значение true, если значение t входит в строку s, изначение false — в противном случае. Предположим, что в программе даныописания:const N=50;type cTpoK.a=packed array C1..N3 of char;Тогда, если эффективность вычисления интересующей нас функции неиграет большой роли, эту функцию можно описать, например, следующим образом:function ВХСИМ<уаг з: отрока; t: char): boolean;var i:1..N;begin ВХСИМ:=false;f or i:=1 to N doif sCi 3=t then BXCHM:=trueendВ этом описании первый оператор присваивания предписывает функции значение false — в предположении, что .задаваемая литера не входитв задаваемую строку.
Далее по оператору цикла элементы строки последовательно сравниваются с литерой t. Если эта литера действительнов строку не входит, то в качестве значения функции сохранится первоначально пердписанное ей значений false. Если же при просмотре строки заданная литера встретится, то первоначальное значение функции будет замененона значение true.Предложенный выше алгоритм может оказаться неэффективным, еслизадаваемая литера встретится в строке достаточно быстро — в этом случаеизлишний просмотр последующих элементов строки и возможное неодно173Jкратное предписывание функции значения true могут занять много времени. Поэтому при первом же обнаружении в строке заданной литеры и корректировке значения функции следовало бы прекратить продолжение выполнение оператора цикла.
В подобного рода случаях, чтобы не усложнятьзапись оператора цикла и не вводить в употребление вспомогательныхпеременных, к а к раз и удобно использование операторов перехода:f u n c t i o n ВХСИМ(var s: строка; t: char): boolean;l a b e l 25;var i : 1 . . N ;b e g i n ВХСИМ: -f alse;f o r i : =1 t o N doif sCi 3=t t h e nb e g i n ВХСИМ:"true; g o t o 25 e n d ;25:endПоскольку по выходе из оператора цикла никаких действий выполнятьне нужно, то меткой 25 помечен пустой оператор.Как мы уже знаем, в паскале предусмотрен определенный набор стандартных функций — эти функции можно использовать в любой программебез их явного описания. Можно считать, что описания всех используемыхв данной программе стандартных функций предварительно вставляютсясамим транслятором в раздел процедур и функций транслируемойпрограммы.9.2. Вызов функцииЕсли для обращения к процедуре-оператору(с целью ее активации) служитоператор процедуры, то дляобращения к процедуре-функции служитвызов функции.
Заметим, что термин " ф у н к ц и я " означает определенныйпрограммный объект, а "вызов функции" — некоторую синтаксическуюконструкцию, с помощью которой в программе задается обращение кэтому программному объекту с целью его активации. Однако рада краткости изложения мы будем иногда использовать термин "функция" и вкачестве синонима для термина "вызов функции", где это не приводит кнеоднозначности понимания.Синтаксически вызов функции определяется точно так же, как и оператор процедуры — это либо имя функции, либо имя функции, за которымследует взятый в круглые скобки список фактических параметров, например:sin(x + у) FACT(9) ВХСИМ (str, '+')Так что без учета контекста довольно трудно различить вызов функции иоператор процедуры.При работе с функциями следует иметь в виду, что процедура-функция,вообще говоря, не задает какого-то логически завершенного этапавычислительного процесса - она задает лишь правило вычисления некоторого значения, принимаемого в качестве значения определяемой функции,174ничего не говоря о том, что следует делать дальше с этим значением (идаже о том, где его следует сохранить для дальнейшего использования,так что транслятор по своему усмотрению может решать вопрос о том,куда помещать вычисленное значение любой функции: в фиксированнуюячейку памяти, в сумматор одноадресной машины, в определенный регистр,который может использоваться для этой цели, и т.д.).
Поэтому и вызовфункции представляет вычисляемое (с помощью соответствующей процедуры-функции) значение, которое обычно используется в качестве операнда какой-либо операции. Необходимо четко помнить, что вызов функцииможет использоваться только в качестве компоненты (возможно, единственной) какого-либо выражения. Для вычисления значения функции(в процессе вычисления того выражения, в которое она входит) производится обращение к соответствующей процедуре-функции — по тем же правилам, что и обращение к процедуре-оператору, так что вычислениефункции сводится к выполнению соответствующего вызову функцииэквивалентного блока. Результат последнего по времени выполнения оператора присваивания вида< имя функции > := (выражение >этого блока принимается в качестве значения функции и это значение используется при продолжении вычисления того выражения, в которое входила эта функция.
Если выражение состоит только из (вызова) функции,то ее значение и принимается в качестве значения выражения.Как уже неоднократно отмечалось, между процедурами-операторами ипроцедурами-функциями много общего как с синтаксической, так и с семантической точек зрения. На самом деле во многих случаях можно почтис одинаковым успехом использовать как те, так и другие процедуры, еслирезультатом выполнения процедуры-оператора является (единственное)скалярное значение. Например, для вычисления р = (к + 1)!, где к и р целочисленные переменные, можно использовать описанную выше функциюFACT — для этого в программе достаточно записать оператор присваиванияpi =FACT<k + l)Можно было бы ввести в употребление и процедуру-оператор, котораявычисляет значение факториала и присваивает это значение некоторойпеременной:procedure FACTOR(n: integer; var m:integer);var i: i nteger;begin m:=l;•for i: = l to n do m:=m*iendВ этом случае для вычисления р = (к + 1)! в программе надо было бы записать оператор процедурыFACTOR(k+l,р>В связи с этим может возникнуть вопрос — какой же вид процедурыследует предпочесть? Ответ на него зависит от того, к а к мы собираемсяиспользовать данную процедуру.175Допустим, что в программе надо вычислить значениер = к\ + 2 + (Зк)\ -5!Как видно, здесь значения fc!, (Зк) ! и 5! являются промежуточными результатами, которые используются только для вычисления значения р.
В данномслучае процедура-оператор FACTOR была бы неудобна: для реализациинужных вычислений пришлось бы ввести в употребление несколько вспомогательных переменных для хранения промежуточных результатов, напримерvar ml,m2,m3:integer;и в программе записать несколько операторов:FACTOR < k , ш 1 > ; FACTOR (3*к , m2) ; FACTOR <5, m3) ; р: =т 1 +2+т2-гпЗИспользование же процедуры-функции FACT не требует вспомогательныхпеременных и позволяет задать требуемые вычисления с помощью единственного оператора присваивания:р:=FACT(k)+2+FACT< 3*к >-FACT <5>Если же в процессе решения задачи надо получитьа = к \ , й = (ЗА:)!, с = 5!где указанные значения переменных а, Ъ, с необходимы по отдельности дляпоследующих вычислений, то процедура-оператор FACTOR очень удобнадля задания этих вычислений:FACTOR < k,а);FACTOR<3*к,b>;FACTOR(5,c>Впрочем, здесь почти столь же удобна и процедура-функция FACT:a:=FACT<k>;b:=FACT(3*k);cs=FACT(5)Таким образом, если вычисляемое процедурой значение чаще используется в качестве промежуточных значений при вычислении выражений, толучше использовать процедуру-функцию.
Если же это значение обычно надоприсвоить некоторой переменной, то можно использовать и процедуруоператор, поручив ей и выполнение присваивания вычисленного значениязадаваемой переменной.Конечно, о предпочтительности того или иного вида процедур можноговорить только в том случае, если результатом является единственноескалярное значение. Если же таким результатом является значениепроизводного типа (например массив), то здесь процедуры-функциивообще не применимы из-за ограничения на допустимые типы значенийфункций "(не говоря уже о процедурах, результатом выполнения которыхявляется некоторое действие — печать какого-то сообщения на АЦПУ,ввод данных и т.п.).9.3. Побочные эффекты функцийХотя термин "главный эффект функции" и не употребляется, этим термином можно бьшо бы назвать вычисление значения функции, которое поставляется в программу в качестве значения вычисляемого операнда в какомлибо выражении.До сих пор мы исходили из того, что этот "главный эффект" функцииявляется и единственным — в том смысле, что вне процедуры-функции176невозможно обнаружить каких-либо последствий ее выполнения, крометого, что оказалось определенным значение соответствующего операнда ввыражении.
Все рассматривавшиеся примеры процедур-функций обладалиименно таким свойством (заметим, что локализованные в процедуре переменные существуют только на время ее выполнения — поскольку привыходе из процедуры эти переменные вообще прекращают свое существование, то факт возникновения этих переменных и присваивания имкаких-то значений невозможно обнаружить вне процедуры).На самом деле паскаль допускает и такие процедуры-функции, которые — наряду с определением значения функции — могут делать ещекое-что, т.е. выполнять такие действия, результат которых обнаруживаетсяи вне процедуры: изменять значения глобальных для нее переменных,производить вывод и т.п.