Глава 8В этой главе:
Вызов пользовательской функции Возвращаемые значения Аргументы Локальные переменные в функциях Полулокальные переменные, созданные при помощи функции local Создаваемые операцией my() переменные файлового уровня Упражнения ФункцииМы уже знакомы со встроенными пользовательскими функциями, например chomp, print и другими, и пользовались ими. Теперь давайте рассмотрим функции, которые вы можете определить сами.Определение пользовательской функции Пользовательская функция, чаще называемая подпрограммой, определяется в Perl-программе с помощью следующей конструкции: sub имя {оператор_1 ;оператор_2 ;оператор 3;} Здесь имя это имя подпрограммы, которое может быть любым именем вроде тех, которые мы давали скалярным переменным, массивам и хешам. Вновь подчеркнем, что эти имена хранятся в другом пространстве имен, поэтому у вас может быть скалярная переменная $fred, массив @fred, хеш %fred, а теперь и подпрограмма fred*.* Более правильно эту подпрограмму следовало бы назвать sfred, но пользоваться такого рода именами приходится редко.Блок операторов, следующий за именем подпрограммы, становится ее определением. Когда эта подпрограмма вызывается, то блок операторов, входящих в нее, выполняется, и вызывающему объекту выдается соответствующее возвращаемое значение (как будет описано ниже). Вот, например, подпрограмма, которая выдает знаменитую фразу: sub say_hello ( print "hello, world!\n"; } Определения подпрограмм могут стоять в любом месте текста программы (при выполнении они пропускаются), но обычно их размещают в конце файла программы, чтобы основная часть программы находилась в начале. (Если вам часто приходилось писать программы на языке Паскаль, можете по привычке поставить свои подпрограммы в начало, а выполняемые операторы в конец. Это ваше дело.)Определения подпрограмм глобальны*; локальных подпрограмм не бывает. Если у вас вдруг появились два определения подпрограмм с одинаковым именем, то второе определение заменяет первое без предупреждения**. В теле подпрограммы вы можете обращаться к переменным, используемым в других частях программы (глобальным переменным), и присваивать им значения. По умолчанию любая ссылка на переменную в теле подпрограммы относится к глобальной переменной. Об исключениях из этого правила мы расскажем в разделе "Локальные переменные в функциях". В следующем примере: sub say_what { print "hello, $what\n"; } переменная $what является глобальной переменной, которая может использоваться также и в других частях программы.Вызов пользовательской функции Для вызова подпрограммы из любого выражения необходимо поставить после ее имени круглые скобки, например: say_hello(); # простое выражение$а = 3 + say_hello() # часть более сложного выраженияfor ($х = start_value(); $х < end_value(); $х += increment О) (} t вызов трех подпрограмм для определения значений* Точнее, глобальны для текущего пакета, но поскольку в этой книге отдельные пакеты не рассматриваются, вы можете считать определения подпрограмм глобальными для всей программы.** Если только вы не выполняете программу с ключом -w.Одна подпрограмма может вызывать другую подпрограмму, которая, в свою очередь, может вызывать третью подпрограмму и т.д., пока вся наличная память не будет заполнена адресами возврата и не полностью вычисленными выражениями. (Ведь настоящего программиста вряд ли удовлетворят какие-то 8 или 32 уровня вложенности подпрограмм.)Возвращаемые значения Вызов подпрограммы почти всегда является частью некоторого выражения. Результат, полученный после выполнения подпрограммы, называется возвращаемым значением. Возвращаемое значение представляет собой результат выполнения оператора return или последнего выражения, вычисленного в подпрограмме.Давайте, например, определим такую подпрограмму: sub sum_of_a and_b ( return $a + $b; } Последнее выражение, вычисляемое в теле этой подпрограммы (фактически единственное вычисляемое выражение), сумма переменных $а и $ь, поэтому эта сумма и будет возвращаемым значением. Вот как все это работает:$а = 3; $b = 4;$с = sum_of_a_and_b(); # $с присваивается значение 7 $d = 3 * sum_of_a_and_b(); # $d содержит значение 21При вычислении в списочном контексте подпрограмма может возвращать список значений. Рассмотрим такую подпрограмму и ее вызов: sub Ii st_o f_a_and_b { return($a,$b) ; } $a = 5; $b = 6; @c = list_of_a_and_b(); # @c присваивается значение (5,6)Последнее вычисленное выражение действительно означает последнее вычисленное выражение, а не последнее выражение, определенное в теле подпрограммы. Например, следующая подпрограмма возвращает $а, если $а > 0; в противном случае она возвращает $Ь:sub gimme a_or_b ( if ($a > 0) ( print "choosing a ($a)\n"; returns $a; ) else ( print "choosing b ($b)\n"; returns $b; } } Все это довольно тривиальные примеры. Однако будет гораздо лучше, если вместо того, чтобы полагаться на глобальные переменные, мы сможем передавать в подпрограмму значения, разные для каждого вызова. Именно к этому мы сейчас и перейдем. Аргументы Несомненно, подпрограммы, выполняющие одно определенное действие, полезны, однако перед вами откроются совершенно новые горизонты, когда вы сможете передавать в подпрограмму аргументы. В Perl после вызова подпрограммы следует список, заключенный в круглые скобки, которые обеспечивают автоматическое присваивание элементов данного списка на период выполнения этой подпрограммы специальной переменной с именем @_. Подпрограмма может обратиться к этой переменной и получить число аргументов и их значения. Например:sub say_hello_to ( print "hello, $_[0]!\n" # первый параметр }Здесь мы видим ссылку на $_[0] первый элемент массива @_. Обратите внимание: несмотря на внешнее сходство, значение $_ [ 0 ] (первый элемент массива @_) не имеет ничего общего с переменной $_ (самостоятельной скалярной переменной). Не путайте их! Из этого кода видно, что подпрограмма приветствует того, чье имя мы указываем в качестве первого параметра. Это значит, что ее можно вызвать так:say_hello_to("world"); # выдает hello, world $х = "somebody";say_hello_to($x); # выдает hello, somebody say_hello_to("me")+say_hello_to("you"); # а теперь приветствует себя и васВ последней строке возвращаемые значения явно использованы не были, однако для определения суммы Perl сначала должен вычислить все ее слагаемые, поэтому подпрограмма была вызвана дважды.Вот пример с использованием более одного параметра: sub say ( print "$_[0], $_[!]!\n"; } say("hello","world"); # опять hello worldsay ("goodbye", "cruel world"); # goodbye cruel world - популярная фраза из фильмовИзбыточные параметры игнорируются, т.е. если вы никогда не заглядываете в $_ [ 3 ], языку Perl это абсолютно все равно. Недостающие параметры также игнорируются, и если вы попытаетесь обратиться за пределы массива @_, как и любого другого массива, то просто получите в ответ undef.Переменная @_ является локальной для подпрограммы. Если для @_ установлено глобальное значение, то оно перед вызовом подпрограммы сохраняется, а после возврата из подпрограммы восстанавливается. Это также означает, что подпрограмма может передавать аргументы в другую подпрограмму, не боясь "потерять" собственную переменную @_; вложенный вызов подпрограммы точно так же получает собственную переменную @_. Давайте вернемся к программе сложения а и b из предыдущего раздела. Вот подпрограмма, которая складывает любые два значения, а именно два значения, передаваемые в нее как параметры:sub add_two { return $_[0] + $_[!]; 1 print add_two(3,4); # выводит значение 7$с = add_two(5,6); # $с получает значение 11Давайте обобщим эту подпрограмму. Что, если нам нужно сложить 3, 4 или 100 значений? Это можно было бы сделать с помощью цикла, например:sub add ( $sum = 0; # инициализировать суммуforeach $_ (@_) { $sum += $_; } return $sum # последнее вычисленное выражение: сумма всех элементов }$а = add(4,5,6); # складывает 4+5+6=15 и присваивает переменной $а print add(1,2,3,4,5); # выводит 15 print add (1..5); # тоже выводит 15, потому что список 1..5 раскрываетсяЧто, если бы переменная с именем $sum использовалась у нас раньше? При вызове подпрограммы add мы просто потеряли бы ее значение. В следующем разделе мы увидим, как избежать этого.Локальные переменные в функциях Мы уже говорили о переменной @_ и о том, как для каждой подпрограммы, вызываемой с параметрами, создается локальная копия этой переменной. Вы можете создавать собственные скалярные переменные, переменные-массивы и хеш-переменные, которые будут работать точно так же. Это делается с помощью операции ту, которая получает список имен переменных и создает их локальные версии (или реализации, если вам так больше нравятся). Вот снова функция add, на этот раз построенная на основе операции ту:sub add { ту ($sum); # сделать $sum локальной переменной $sum =0; # инициализировать сумму* foreach $_ (@_) ( $sum += $ ; # прибавить все элементы} return $sum # последнее вычисленное выражение: сумма всех элементов }Когда выполняется первый оператор в теле подпрограммы, текущее значение глобальной переменной $sum сохраняется и создается совершенно новая переменная с именем $sum (и значением undef). При выходе из подпрограммы Perl отбрасывает эту локальную переменную и восстанавливает предыдущее (глобальное) значение. Эта схема работает даже в том случае, если переменная $sum в текущий момент является локальной переменной, взятой из другой подпрограммы (той, которая вызывает данную подпрограмму, или той, которая вызывает ту, которая вызывает данную подпрограмму, и т.д.). Переменные могут иметь много вложенных локальных версий, но одновременно разрешается обращаться только к одной из них.Вот способ создания списка всех элементов массива, значения которых превышают число 100:sub bigger_than_100 ( my (Oresult); # push(@result,$_); # прибавить } )return Oresult; # возвратить окончательный список }Что, если бы нам понадобились все элементы, значения которых превышают 50, а не 100? Пришлось бы отредактировать программу, заменив 100 на 50. А если бы нам было нужно и то, и другое? В этом случае следовало бы заменить 50 или 100 ссылкой на переменную. Тогда программа выглядела бы так:sub bigger_than ( my($n,@values); # # и элементы массива my (@result); # временная переменная для хранения возвращаемого4 значенияforeach $_ (@values) ( # проход по списку аргументов if ($_ > $n) { # подходит?push(Oresult,$_); # прибавить } )@result; t возвратить окончательный список }# примеры вызова :@new = bigger_than(100,@list); # @new содержит все значения Olist > 100 @this " bigger_than(5,l,5,15,30); # @this содержит значение (15,30)Обратите внимание: в этот раз мы использовали еще две локальные переменные, чтобы присвоить имена аргументам. Это весьма распространенная практика, потому что гораздо удобнее (да и безопаснее) использовать переменные $п и $values, чем указывать $_[0] и @_[1. . $#_].В результате выполнения операции my создается список, который можно использовать в левой части операции присваивания. Этому списку можно присваивать начальные значения для каждой из вновь созданных переменных. (Если значения списку не присвоить, то новые переменные вначале получат значение undef, как и любая другая переменная.) Это значит, что мы можем объединить первые два оператора этой подпрограммы, т.е. операторыmy($n,@values) ; ($n,@values) = заменить на my($n,@values)= @_; Этот прием очень распространен в Perl. Локальным переменным, не являющимся аргументами, можно таким же способом присваивать литеральные значения, например:my($sum) = 0; # инициализировать локальную переменнуюИмейте в виду, что, несмотря на наличие объявления, my в действительности представляет собой выполняемую операцию. Стратегия правильной работы в Perl состоит в том, что все операции my должны быть размещены в начале определения подпрограммы, до того, как начинается реализация основных выполняемых в ней действий.Полулокальные переменные, созданные при помощи функции localВ Perl существует еще один способ создания "частных" локальных переменных с помощью функции local. Важно понять различия между my и local. Например:$values = "original"; tellmeO ; spoof() ; tellmeO ; sub spoof ( local ($value) = "temporary"; tellmeO ; > sub telime { print "Current value is $value\n"; } На выходе получаем следующее: Current value is original Current value is temporary Current value is original Если бы вместо local мы использовали my, то локальный вариант переменной $value был бы доступен только в пределах подпрограммы spoof (). При использовании функции local, как видно по результатам программы, это локальное значение не совсем локальное; оно доступно и во всех остальных подпрограммах, вызываемых из spoof (). Общее правило таково: локальные переменные доступны для функций, вызываемых из того блока, в котором эти функции объявлены.Операцию my можно использовать только для объявления простых скалярных переменных, переменных-массивов и хеш-переменных с буквен-но-цифровыми именами, тогда как для переменной local такие ограничения не установлены. Кроме того, встроенные переменные Perl, такие как $_,#1 и @argv, с помощью my объявлять нельзя, а вот с local они работают прекрасно. Поскольку $_ используется в большинстве Perl-программ, то будет, вероятно, разумным помещать строкуlocal $_; в начало любой подпрограммы, которая использует $_ для собственных нужд. Это гарантирует сохранность предыдущего значения и его автоматическое восстановление при выходе из подпрограммы.На более серьезном уровне программирования вам, возможно, нужно будет знать, что переменные, создаваемые функцией local, это, по сути дела, замаскированные глобальные переменные, т.е. значение глобальной переменной сохраняется и временно заменяется локально объявленным значением.В большинстве случаев рекомендуем использовать не local, a my, потому что эта операция действует быстрее и надежнее.Создаваемые операцией my() переменные файлового уровня Операцию my () можно также использовать на внешнем уровне программы, вне всех подпрограмм и блоков. Хотя в результате не будет получена "локальная" переменная в описанном выше смысле, это может оказаться достаточно полезным, особенно при использовании в сочетании с Рет\-прагмой*use strict; * Прагма это директива компилятора. Среди этих директив директивы задания целочисленных арифметических операций, перегрузки числовых операций, запрашивания дополнительных текстовых предупреждений и сообщений об ошибках. Эти директивы описаны в главе 7 книги Programming Perl и на man-странице perlmodlib(l).Если поставить эту прагму в начало файла, то вы больше не сможете использовать переменные (скалярные, массивы и хеши), сначала не "объявив" их. Объявляются они с помощью операции my () следующим образом:use strict; my $а; # сначала значение undef my @b = qw(fred barney betty); # присвоить начальное значениеpush @b, qw(wilma); # разве можно забыть про Вильму? @с = sort @b; # HE КОМПИЛИРУЕТСЯВо время компиляции последний оператор будет помечен флагом ошибки, потому что он обратился к переменной, не объявленной ранее с помощью операции my (т.е. @с). Другими словами, ваша программа даже не начнет работать до тех пор, пока не будут объявлены все используемые переменные. Преимущества принудительного объявления переменных таковы: 1. Ваши программы будут работать несколько быстрее (обращение к переменным, созданным с помощью my, производится несколько быстрее, чем к обычным переменным).*2. Гораздо быстрее будут выявляться ошибки набора, потому что вы большене сможете случайно обратиться к несуществующей переменной $ freed,когда вам будет нужна переменная $fred.По этой причине многие программисты, пишущие на Perl, автоматически начинают каждую новую программу прагмой use strict.Упражнения Ответы к упражнениям даны в приложении А. 1. Напишите подпрограмму, которая будет принимать в качестве аргумента числовое значение от 1 до 9 и возвращать английское название этого числа (т.е. one, two и т.д.). Если значение не принадлежит указанному диапазону, подпрограмма должна возвратить вместо имени исходное число. Проверьте работу подпрограммы, введя какие-нибудь данные. Для вызова этой подпрограммы вам, наверное, придется написать какой-то код. (Совет: эта подпрограмма не должна выполнять никакого ввода-вывода.)2. Взяв подпрограмму из предыдущего упражнения, напишите программу, которая будет брать два числа и складывать их, выдавая результат в формате Two plus two equals four. (He забудьте начать первое слово с заглавной буквы!)3. Модернизируйте эту подпрограмму так, чтобы она возвращала названия от negative nine до negative one и zero (т.е. принимала числа из диапазона от -9 до -1 и нуль). Проверьте, как она будет работать в программе из упражнения 2.* "Обычная переменная" в этом случае переменная пакета (поэтому $х это, по сути дела, $main:: х). Переменные, созданные с помощью my (), ни в один пакет не входят. |