Песни о Паскале — страница 25 из 112


      x:= 1+ Random(10);       { арифметическое выражение }

      Writeln(Length(S)); { печатается длина строки S };


Функции избавляют программиста от объявления лишних переменных, упрощая программы и повышая их надежность. Сейчас мы научимся создавать собственные функции.

Объявление функции

Подобно объявлению процедуры, объявление функции состоит из заголовка и тела. Тело строят по тем же правилам, что и для процедур, а вот заголовок выглядит немного иначе.


function Имя_Функции : Тип;       { функция без параметров }

function Имя_Функции (Параметры) : Тип;       { функция с параметрами }


Отличий от процедуры всего два. Во-первых, вместо ключевого слова PROCEDURE указано слово FUNCTION. А во-вторых, завершает заголовок тип функции (тип возвращаемого ею результата), – его указывают после двоеточия.

Пример функции

Разберем все это на примере. Создадим функцию, выбирающую большее из двух чисел. Разумеется, что функция будет принимать два параметра – сравниваемые числа, и возвращать будет тоже число. Стало быть, её заголовок может быть таким:


function Max(arg1, arg2 : integer) : integer;


Имя функции выбираем на свой вкус, здесь имя Max вполне подходит, оно означает MAXIMUM (наибольший). К этому заголовку прилепим тело функции, состоящее из одного условного оператора.


function Max(arg1, arg2 : integer) : integer;

begin

      if arg1 > arg2

      then Max:= arg1

      else Max:= arg2

end;


Но откуда взялась переменная Max, которой присваиваем значение? Ведь мы её не объявляли! А её и не надо объявлять, – это имя нашей функции, оно и принимает в себя результат. Мало того, если результату не присвоить значение, он останется неопределенным, и это будет ошибкой!

Созданная нами функция может вызываться так:


      A:= Max( 20, 10 );       { A = 20 }

      Writeln( Max( A, B ) );       { печатается большее из A и B }


Вызов функции можно использовать даже как фактический параметр в её собственном вызове, то есть организовать вложенные вызовы, например:


      A:= Max ( Max( 20, 10 ), 40 );       { A = 40 }

      A:= Max ( Max( 20, 10 ), Max( 200, 100 ) ); { A = 200 }


В первом случае сначала вызывается функция Max(20,10), вставленная как первый фактический параметр, а затем Max(20,40), – то есть результат первого вызова подставляется параметром во второй. Похоже работает и другой пример, только функция вызывается трижды. Полезно понаблюдать за такими вызовами через отладчик. Напишите главную программу для исследования функции Max и прогоните её в отладчике.

Подсчет символов в строке

В прошлой главе я предложил вам написать процедуру для подсчета заданного символа в строке. Если вы справились с той задачей, то для возврата результата наверняка воспользовались ссылкой на переменную. Теперь рассмотрим решение с применением функции.

Начнем, разумеется, с заголовка функции, дадим ей имя Count (подсчет).


function Count(const Str : string; Ch : char): integer;


Функция принимает два параметра: ссылку на строку и символ, который надо подсчитать. Напомню, что ключевое слово CONST в объявлении параметра позволяет ссылаться и на константу, и на переменную. Тело функции строим на базе цикла со счетчиком.


function Count(const str : string; ch: char): integer;

var N, i: integer;

begin

N:=0; { обнуляем счетчик }

for i:=1 to Length(str) do

      if str[i]=ch then N:= N+1;

Count:= N; { определяем результат функции }

end;


Подсчет символов в массиве ведется в локальной переменной N, и лишь по завершении цикла результат копируется в имя функции. Грубой ошибкой было бы накапливать счетчик прямо в имени функции:


      if str[i]=ch then Count:= Count+1; { – это ошибка! }


Запомните: в теле функции её имя применяется только слева от оператора присваивания! Есть исключения из этого правила, но мы пока не будем их касаться.

И, наконец, напишем программу «P_22_1» для проверки функции Count. В главной программе функция вызывается сначала для переменной S, а затем для константы «BANAN». Причем во втором случае она вызывается дважды, а результат суммируется. Испытайте эту программу.


{ P_23_1 – подсчет заданных символов в строке }


function Count(const str : string; ch: char): integer;

var N, i: integer;

begin

N:=0; { обнуляем счетчик }

for i:=1 to Length(str) do

      if str[i]=ch then N:= N+1;

Count:= N; { передаем результат через имя функции }

end;

var S: string;

begin {--- главная программа ---}

S:='PASCAL';

Writeln( Count(S, 'A'));

Writeln( Count('BANAN', 'N') + Count('BANAN', 'B'));

Readln;

end.


Возврат строк

Вернемся к программе «P_20_1», заменяющей символы «A» на символы «B». Помните сколько крови она попортила прежде чем заработать? Заменив процедуру Scan на функцию с тем же именем, мы решим проблему возврата результата. Результат, разумеется, должен иметь строковый тип. Обратите внимание на то, что ключевые слова VAR или CONST в заголовке не указаны, а потому параметр arg можно употребить в теле функции в качестве локальной переменной.


{ P_23_2 – замена символов в строке с применением функции }


function Scan(arg : string): string;

var k: integer;

begin

      for k:=1 to Length(arg) do

      if arg[k]=’A’ then arg[k]:=’B’; { замена в параметре arg }

Scan:= arg;

end;

var S: string; k: integer;

begin {--- главная программа –--}

      for k:=1 to 3 do begin

      Write(’Введите строку: ’); Readln(S);

      Writeln(Scan(S));

      end;

      Readln;

end.


Когда результат не важен

Хорошая функция возвращает правильный результат, а отличная делает ещё что-нибудь полезное. Программисты нередко поручают одной функции несколько дел, вот пример: напишем функцию Swap (обмен) булевого типа, принимающую ссылки на две переменные. Функция должна сравнить эти переменные и вернуть TRUE, если первая из них окажется больше второй. Мало того, в этом случае она должна обменять значения этих переменных (как в процедуре Swap, рассмотренной ранее). Короче, функция будет такой.


function Swap( var a1, a2 : integer) : Boolean;

var t: integer;

begin

      if a1 > a2

      then begin

      { обмен значений переменных }

      t:=a1; a1:=a2; a2:=t;

      Swap:= true

      end

      else Swap:= false

end;


Где применить такую функцию? Пусть переменные N1, N2, N3 содержат три разных числа. Переложим эти числа так, чтобы в N1 оказалось наименьшее, а в N3 – наибольшее число, то есть, чтобы соблюдалось условие: N1 < N2 < N3. Такая сортировка выполняется тремя вызовами функции Swap (в комментариях показаны результаты обмена).


Swap(N1, N2);       { N1 < N2 }

      if Swap(N2, N3)       { N2 < N3 }

      then Swap(N1, N2);       { N1 < N2 < N3 }


Здесь в первой и третьей строках функция вызывается как процедура, поскольку возвращаемый ею булев результат не используется. Во второй строке она вызывается как функция, поскольку результат использован оператором IF.

Возможность вызывать функцию как процедуру называют расширенным синтаксисом (Extended syntax), – он должен быть разрешен в настройках компилятора, иначе вызов функции как процедуры компилятор сочтет ошибкой.

Неявная переменная Result