perlfaq7
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ОПИСАНИЕ
- Можно ли получить BNF/yacc/RE для языка Perl?
- Какие все эти знаки пунктуации $@%&*, и как я узнаю, когда их использовать?
- Должен ли я всегда/никогда заключать свои строки в кавычки или использовать точки с запятой и запятые?
- Как пропустить некоторые возвращаемые значения?
- Как временно заблокировать предупреждения?
- Что такое расширение?
- Почему операторы Perl имеют разный приоритет по сравнению с операторами C?
- Как объявить/создать структуру?
- Как создать модуль?
- Как принять или взять на себя модуль, уже находящийся на CPAN?
- Как создать класс?
- Как узнать, является ли переменная заражённой?
- Что такое замыкание?
- Что такое самоубийство переменной и как его предотвратить?
- Как передать/вернуть {Функцию, дескриптор файла, массив, хеш, метод, регулярное выражение}?
- Как создать статическую переменную?
- В чем разница между динамическим и лексическим (статическим) охватом? Между local() и my()?
- Как получить доступ к динамической переменной, пока аналогично названная лексическая находится в области видимости?
- В чем разница между глубокой и поверхностной привязкой?
- Почему "my($foo) = <$fh>;" не работает правильно?
- Как переопределить встроенную функцию, оператор или метод?
- В чем разница между вызовом функции как &foo и foo()?
- Как создать оператор switch или case?
- Как перехватить обращения к неопределенным переменным, функциям или методам?
- Почему метод, включенный в этот же файл, не найден?
- Как узнать мой текущий или вызывающий пакет?
- Как закомментировать большой блок кода Perl?
- Как очистить пакет?
- Как использовать переменную как имя переменной?
- Что означает "bad interpreter"?
- Нужно ли перекомпилировать модули XS при изменении библиотеки C?
- АВТОР И АВТОРСКИЕ ПРАВА
НАЗВАНИЕ
perlfaq7 - Общие вопросы языка Perl
ВЕРСИЯ
версия 5.021011
ОПИСАНИЕ
Этот раздел посвящен общим вопросам языка Perl, которые не явно подходят ни к одному из других разделов.
Можно ли получить BNF/yacc/RE для языка Perl?
BNF нет, но вы можете попытаться разобраться в грамматике yacc в файле perly.y в дистрибутиве исходных кодов, если вы достаточно смелы. Грамматика полагается на очень умный код токенизации, поэтому будьте готовы углубиться и в toke.c.
По словам Хайма Френкеля: "Грамматика Perl не может быть сведена к BNF. Работа по разбору perl распределена между yacc, лексическим анализатором, дымом и зеркалами."
Какие все эти знаки пунктуации $@%&*, и как я узнаю, когда их использовать?
Это спецификаторы типов, как подробно описано в perldata:
$ for scalar values (number, string or reference)
@ for arrays
% for hashes (associative arrays)
& for subroutines (aka functions, procedures, methods)
* for all types of that symbol name. In version 4 you used them like
pointers, but in modern perls you can just use references. Есть еще несколько символов, которые вы, вероятно, встретите, которые не являются спецификаторами типов:
<> are used for inputting a record from a filehandle.
\ takes a reference to something. Обратите внимание, что <FILE> не является спецификатором типа для файлов, ни именем дескриптора. Это <> оператор, примененный к дескриптору FILE. Он считывает одну строку (ну, запись - см. "$/" в perlvar) из дескриптора FILE в скалярном контексте или все строки в списочном контексте. При выполнении открывания, закрытия или любой другой операции, кроме <> с файлами, или даже при обсуждении дескриптора, не используйте скобки. Это верно: eof(FH), seek(FH, 0, 2) и "копирование из STDIN в FILE".
Должен ли я всегда/никогда заключать свои строки в кавычки или использовать точки с запятой и запятые?
Обычно, нецитируемое слово не требует кавычек, но в большинстве случаев, вероятно, стоит это сделать (и это необходимо в use strict). Но ключ хеша, состоящий из простого слова, и левая часть операнда к => оператору оба считаются как бы цитированными:
This is like this
------------ ---------------
$foo{line} $foo{'line'}
bar => stuff 'bar' => stuff Окончательная точка с запятой в блоке необязательна, как и окончательная запятая в списке. Хороший стиль (см. perlstyle) предполагает, что они должны быть, за исключением однострочников:
if ($whoops) { exit 1 }
my @nums = (1, 2, 3);
if ($whoops) {
exit 1;
}
my @lines = (
"There Beren came from mountains cold",
"And lost he wandered under leaves",
); Как пропустить некоторые возвращаемые значения?
Один способ - рассматривать возвращаемые значения как список и обращаться к ним по индексу:
$dir = (getpwnam($user))[7]; Другой способ - использовать undef в качестве элемента в левой части:
($dev, $ino, undef, undef, $uid, $gid) = stat($file); Вы также можете использовать срез списка, чтобы выбрать только необходимые элементы:
($dev, $ino, $uid, $gid) = ( stat($file) )[0,1,4,5]; Как временно заблокировать предупреждения?
Если вы используете Perl 5.6.0 или более позднюю версию, pragma use warnings позволяет тонко управлять созданием предупреждений. Более подробную информацию см. в perllexwarn.
{
no warnings; # temporarily turn off warnings
$x = $y + $z; # I know these might be undef
} Кроме того, можно включать и отключать категории предупреждений. Отключите те категории, которые хотите игнорировать, и вы все еще можете получить другие категории предупреждений. Полные детали, включая имена категорий и иерархию, см. в perllexwarn.
{
no warnings 'uninitialized';
$x = $y + $z;
} Если у вас более старая версия Perl, переменная $^W (документирована в perlvar) управляет предупреждениями во время выполнения для блока:
{
local $^W = 0; # temporarily turn off warnings
$x = $y + $z; # I know these might be undef
} Обратите внимание, что, как и все пунктуационные переменные, вы не можете использовать my() с $^W, только local().
Что такое расширение?
Расширение - это способ вызова скомпилированного кода C из Perl. Чтение perlxstut - хорошее место, чтобы узнать больше о расширениях.
Почему операторы Perl имеют разный приоритет по сравнению с операторами C?
На самом деле, нет. Все операторы C, которые Perl копирует, имеют тот же приоритет в Perl, что и в C. Проблема в операторах, которых нет в C, особенно функциях, которые предоставляют списочный контекст всему справа, например, print, chmod, exec и так далее. Такие функции называются "операторными списками" и появляются как таковые в таблице приоритетов в perlop.
Частая ошибка: написать:
unlink $file || die "snafu"; Это интерпретируется как:
unlink ($file || die "snafu"); Чтобы избежать этой проблемы, либо добавьте дополнительные скобки, либо используйте оператор с очень низким приоритетом or:
(unlink $file) || die "snafu";
unlink $file or die "snafu"; Операторы "English" (and, or, xor, и not) намеренно имеют более низкий приоритет, чем операторы списков, именно для таких ситуаций, как описанная выше.
Еще один оператор с неожиданным приоритетом - возведение в степень. Он связывается сильнее, чем даже унарный минус, что делает -2**2 равным -4, а не +1. Он также является правоассоциативным, что означает, что 2**3**2 - это два в девятой степени, а не восемь в квадрате.
Хотя он имеет тот же приоритет, что и в C, оператор ?: Perl возвращает lvalue. Это присваивает $x либо $if_true, либо $if_false, в зависимости от истинности $maybe:
($maybe ? $if_true : $if_false) = $x; Как объявить/создать структуру?
В общем, вы не "объявляете" структуру. Просто используйте (вероятно, анонимную) ссылку на хеш. Подробнее см. perlref и perldsc. Вот пример:
$person = {}; # new anonymous hash
$person->{AGE} = 24; # set field AGE to 24
$person->{NAME} = "Nat"; # set field NAME to "Nat" Если вы ищете что-то более строгое, обратитесь к perlootut.
Как создать модуль?
perlnewmod - хорошее место для начала, игнорируйте части об отправке на CPAN, если вы не хотите сделать ваш модуль общедоступным.
ExtUtils::ModuleMaker и Module::Starter также являются хорошими местами для начала. Многие авторы с CPAN теперь используют Dist::Zilla, чтобы автоматизировать как можно больше.
Подробная документация по модулям находится по адресам: perlmod, perlmodlib, perlmodstyle.
Если вам нужно включить код C или интерфейсы библиотек C, используйте h2xs. h2xs создаст структуру модуля и начальные файлы интерфейса. perlxs и perlxstut объясняют подробности.
Как принять или захватить модуль, уже имеющийся на CPAN?
Попросите текущего основного разработчика сделать вас со-разработчиком или перенести модуль на вас.
Если по какой-то причине вы не можете связаться с автором, обратитесь к администраторам PAUSE по адресу modules@perl.org, которые могут помочь, но каждый случай рассматривается индивидуально.
-
Получите учетную запись для сервера загрузки авторов Perl (PAUSE), если у вас её ещё нет: http://pause.perl.org
-
Напишите на modules@perl.org, объяснив, что вы предприняли для связи с текущим основным разработчиком. Администраторы PAUSE также попытаются связаться с основным разработчиком.
-
Опубликуйте сообщение в популярном сообществе, объявив о вашем намерении захватить модуль.
-
Подождите некоторое время. Администраторы PAUSE не хотят действовать слишком быстро, на случай, если текущий основной разработчик в отпуске. Если на частное сообщение или публичное объявление не последует ответа, администратор PAUSE может перенести его на вас.
Как создать класс?
(предоставлено brian d foy)
В Perl, класс — это просто пакет, а методы — просто подпрограммы. Perl не делает ничего сложнее этого и позволяет настроить пакет так, как вам нужно (то есть, он ничего за вас не настраивает).
См. также perlootut, учебник, который охватывает создание классов, и perlobj.
Как определить, является ли переменная заражённой?
Вы можете использовать функцию tainted() модуля Scalar::Util, доступного на CPAN (или включенного в Perl начиная с версии 5.8.0). См. также "Стирка и обнаружение заражённых данных" в perlsec.
Что такое замыкание?
Замыкания документированы в perlref.
Замыкание — это термин компьютерных наук с точным, но труднообъяснимым значением. Обычно замыкания в Perl реализуются как анонимные подпрограммы с сохраняющимися ссылками на лексические переменные за пределами собственной области видимости. Эти лексические переменные магически ссылаются на переменные, которые существовали во время определения подпрограммы (глубокая связь).
Замыкания чаще всего используются в языках программирования, где возвращаемое значение функции может само быть функцией, как это возможно в Perl. Обратите внимание, что некоторые языки предоставляют анонимные функции, но не способны предоставлять правильные замыкания: например, язык Python. Для получения дополнительной информации о замыканиях ознакомьтесь с любой книгой по функциональному программированию. Язык Scheme не только поддерживает, но и поощряет замыкания.
Вот классическая функция, генерирующая функцию, не являющуюся замыканием:
sub add_function_generator {
return sub { shift() + shift() };
}
my $add_sub = add_function_generator();
my $sum = $add_sub->(4,5); # $sum is 9 now. Анонимная подпрограмма, возвращённая add_function_generator(), технически не является замыканием, потому что она не ссылается ни на какие лексические переменные за пределами своей собственной области видимости. Использование замыкания предоставляет вам шаблон функции с некоторыми местами для настройки, которые остаются пустыми, чтобы заполниться позже.
Протестируйте это с помощью следующей функции make_adder(), в которой возвращаемая анонимная функция содержит ссылку на лексическую переменную за пределами области видимости этой функции. Такая ссылка требует, чтобы Perl возвратил правильное замыкание, тем самым навсегда фиксируя значение лексической переменной, существовавшее при создании функции.
sub make_adder {
my $addpiece = shift;
return sub { shift() + $addpiece };
}
my $f1 = make_adder(20);
my $f2 = make_adder(555); Теперь $f1->($n) всегда равно 20 плюс любое значение $n, которое вы передаёте, тогда как $f2->($n) всегда равно 555 плюс любое значение $n, которое вы передаёте. Переменная $addpiece в замыкании сохраняется.
Замыкания часто используются и для менее эзотерических целей. Например, когда вы хотите передать немного кода в функцию:
my $line;
timeout( 30, sub { $line = <STDIN> } ); Если бы код для выполнения передавался в виде строки, '$line = <STDIN>', гипотетическая функция timeout() не смогла бы получить доступ к лексической переменной $line в области видимости вызвавшей её функции.
Другое применение замыканий — сделать переменную приватной для именованной подпрограммы, например, счётчик, который инициализируется во время создания подпрограммы и может изменяться только изнутри этой подпрограммы. Это иногда используется с блоком BEGIN в файлах пакетов, чтобы убедиться, что переменная не была изменена в течение жизненного цикла пакета:
BEGIN {
my $id = 0;
sub next_id { ++$id }
} Это обсуждается более подробно в perlsub; см. запись о постоянных приватных переменных.
Что такое самоубийство переменных и как его предотвратить?
Эта проблема была решена в perl 5.004_05, поэтому её предотвращение означает обновление вашей версии perl. ;)
Самоубийство переменных — это когда вы (временна или постоянно) теряете значение переменной. Оно вызывается взаимодействием области видимости my() и local() с замыканиями или алиасами переменных-итераторов foreach() и аргументами подпрограмм. Раньше было легко ненамеренно потерять значение переменной таким образом, но теперь это значительно сложнее. Рассмотрим этот код:
my $f = 'foo';
sub T {
while ($i++ < 3) { my $f = $f; $f .= "bar"; print $f, "\n" }
}
T;
print "Finally $f\n"; Если у вас возникает самоубийство переменных, то my $f в подпрограмме не получает свежей копии $f, значение которой равно 'foo'. Вывод показывает, что внутри подпрограммы значение $f просачивается туда, где оно не должно быть, как в этом выводе:
foobar
foobarbar
foobarbarbar
Finally foo Переменная $f, к которой трижды добавляется "bar", должна быть новой $f my $f должна создавать новую лексическую переменную на каждой итерации цикла. Ожидаемый вывод:
foobar
foobar
foobar
Finally foo Как передавать/возвращать {Функцию, дескриптор файла, массив, хеш, метод, регулярное выражение}?
Вам нужно передавать ссылки на эти объекты. См. "Передача по ссылке" в perlsub по этому вопросу и perlref для информации о ссылках.
- Передача переменных и функций
-
Обычные переменные и функции довольно легко передавать: просто передайте ссылку на существующую или анонимную переменную или функцию:
func( \$some_scalar ); func( \@some_array ); func( [ 1 .. 10 ] ); func( \%some_hash ); func( { this => 10, that => 20 } ); func( \&some_func ); func( sub { $_[0] ** $_[1] } ); - Передача дескрипторов файлов
-
Начиная с Perl 5.6, вы можете представлять дескрипторы файлов с помощью скалярных переменных, которые обрабатываются как любые другие скаляры.
open my $fh, $filename or die "Cannot open $filename! $!"; func( $fh ); sub func { my $passed_fh = shift; my $line = <$passed_fh>; }До Perl 5.6 вам нужно было использовать обозначения
*FHили\*FH. Это "типизированные ссылки" — см. "Типизированные ссылки и дескрипторы файлов" в perldata и особенно "Передача по ссылке" в perlsub для получения дополнительной информации. - Передача регулярных выражений
-
Вот пример того, как передать строку и регулярное выражение для её сопоставления. Вы создаёте шаблон с помощью оператора
qr//.sub compare { my ($val1, $regex) = @_; my $retval = $val1 =~ /$regex/; return $retval; } $match = compare("old McDonald", qr/d.*D/i); - Передача методов
-
Чтобы передать метод объекта в подпрограмму, вы можете сделать это:
call_a_lot(10, $some_obj, "methname") sub call_a_lot { my ($count, $widget, $trick) = @_; for (my $i = 0; $i < $count; $i++) { $widget->$trick(); } }Или вы можете использовать замыкание для объединения объекта, его вызова метода и аргументов:
my $whatnot = sub { $some_obj->obfuscate(@args) }; func($whatnot); sub func { my $code = shift; &$code(); }Вы также можете изучить метод can() в классе UNIVERSAL (часть стандартного дистрибутива Perl).
Как создать статическую переменную?
(предоставлено brian d foy)
В Perl 5.10 объявите переменную с state. Объявление state создаёт лексическую переменную, которая сохраняется между вызовами подпрограммы:
sub counter { state $count = 1; $count++ } Вы можете имитировать статическую переменную, используя лексическую переменную, которая выходит из области видимости. В этом примере вы определяете подпрограмму counter, и она использует лексическую переменную $count. Поскольку вы помещаете это в блок BEGIN, $count определена во время компиляции, но также выходит из области видимости в конце блока BEGIN. Блок BEGIN также гарантирует, что подпрограмма и используемая ею переменная определены во время компиляции, так что подпрограмма готова к использованию как любая другая подпрограмма, и вы можете поместить этот код в то же место, что и другие подпрограммы в тексте программы (то есть в конце кода, как правило). Подпрограмма counter всё ещё имеет ссылку на данные и является единственным способом доступа к значению (и каждый раз, когда вы это делаете, вы увеличиваете значение). Данные в выделенном куске памяти, определённом $count, приватны для counter.
BEGIN {
my $count = 1;
sub counter { $count++ }
}
my $start = counter();
.... # code that calls counter();
my $end = counter(); В предыдущем примере вы создали переменную, приватную для функции, потому что только одна функция помнила её ссылку. Вы могли бы определить несколько функций, пока переменная находится в области видимости, и каждая функция может использовать "приватную" переменную. Это не совсем "статично", потому что вы можете получить к ней доступ за пределами функции, пока лексическая переменная находится в области видимости, и даже создать ссылки на неё. В этом примере increment_count и return_count используют переменную. Одна функция добавляет к значению, а другая просто возвращает значение. Они оба могут получить доступ к $count, и поскольку она вышла из области видимости, больше нет других способов к ней получить доступ.
BEGIN {
my $count = 1;
sub increment_count { $count++ }
sub return_count { $count }
} Для объявления переменной, приватной для файла, вы всё равно используете лексическую переменную. Файл также является областью видимости, поэтому лексическая переменная, определённая в файле, не видна из других файлов.
См. "Постоянные приватные переменные" в perlsub для получения дополнительной информации. Обсуждение замыканий в perlref может помочь, даже если в этом ответе мы не использовали анонимные подпрограммы. См. "Постоянные приватные переменные" в perlsub для подробностей.
В чём разница между динамической и лексической (статической) областью видимости? Между local() и my()?
local($x) сохраняет старое значение глобальной переменной $x и присваивает новое значение на период существования подпрограммы которое видимо в других функциях, вызываемых из этой подпрограммы. Это делается во время выполнения, поэтому называется динамической областью видимости. local() всегда воздействует на глобальные переменные, также называемые пакетом переменными или динамическими переменными.
my($x) создаёт новую переменную, видимую только в текущей подпрограмме. Это делается во время компиляции, поэтому называется лексической или статической областью видимости. my() всегда воздействует на приватные переменные, также называемые лексическими переменными или (неправильно) статическими переменными.
Например:
sub visible {
print "var has value $var\n";
}
sub dynamic {
local $var = 'local'; # new temporary value for the still-global
visible(); # variable called $var
}
sub lexical {
my $var = 'private'; # new private variable, $var
visible(); # (invisible outside of sub scope)
}
$var = 'global';
visible(); # prints global
dynamic(); # prints local
lexical(); # prints global Обратите внимание, что нигде не печатается значение "private". Это потому, что $var имеет это значение только внутри блока функции lexical(), и оно скрыто от вызываемой подпрограммы.
В заключение, local() не создаёт то, что вы считаете приватными локальными переменными. Она даёт глобальной переменной временное значение. my() — то, что вам нужно, если вы хотите приватные переменные.
См. "Приватные переменные через my()" в perlsub и "Временные значения через local()" в perlsub для подробного описания.
Как получить доступ к динамической переменной, когда в области видимости находится лексическая переменная с похожим именем?
Если вы знаете свой пакет, вы можете просто явно указать его, как в $Some_Pack::var. Обратите внимание, что обозначение $::var не является динамической переменной $var в текущем пакете, а скорее той, что в пакете «main», как если бы вы написали $main::var.
use vars '$var';
local $var = "global";
my $var = "lexical";
print "lexical is $var\n";
print "global is $main::var\n"; В качестве альтернативы, вы можете использовать директиву компилятора our(), чтобы добавить динамическую переменную в текущую область видимости.
require 5.006; # our() did not exist before 5.6
use vars '$var';
local $var = "global";
my $var = "lexical";
print "lexical is $var\n";
{
our $var;
print "global is $var\n";
} В чем разница между глубокой и мелкой привязкой?
При глубокой привязке лексические переменные, упомянутые в анонимных подпрограммах, — это те же переменные, которые были в области видимости при создании подпрограммы. При мелкой привязке — это те переменные с такими же именами, которые оказываются в области видимости при вызове подпрограммы. Perl всегда использует глубокую привязку лексических переменных (т. е. созданных с помощью my()). Однако динамические переменные (также известные как глобальные, локальные или переменные пакета) фактически привязаны мелко. Подумайте об этом как еще об одной причине, по которой не стоит их использовать. Смотрите ответ на "Что такое замыкание?".
Почему «my($foo) = <$fh>;» работает неверно?
my() и local() дают контекст списка правой части =. Операция чтения <$fh>, как и многие функции и операторы Perl, может определить контекст, в котором она была вызвана, и соответствующим образом себя вести. В общем случае функция scalar() может помочь. Эта функция не производит никаких действий с данными (вопреки распространенному мифу), а лишь сообщает своему аргументу, как себя вести в скалярном контексте. Если у этой функции нет определенного скалярного поведения, это, конечно, не поможет (например, с sort()).
Для принудительного скалярного контекста в этом конкретном случае достаточно просто опустить скобки:
local($foo) = <$fh>; # WRONG
local($foo) = scalar(<$fh>); # ok
local $foo = <$fh>; # right Вероятно, вы всё равно должны использовать лексические переменные, хотя проблема здесь та же:
my($foo) = <$fh>; # WRONG
my $foo = <$fh>; # right Как переопределить встроенную функцию, оператор или метод?
Зачем вам это нужно? :-)
Если вы хотите переопределить предопределенную функцию, например open(), то вам нужно будет импортировать новое определение из другого модуля. См. "Переопределение встроенных функций" в perlsub.
Если вы хотите перегрузить оператор Perl, например + или **, тогда вам понадобится использовать псевдоним use overload, документированный в overload.
Если речь идёт о скрытии вызовов методов в родительских классах, см. "Переопределение методов и разрешение методов" в perlootut.
В чем разница между вызовом функции как &foo и foo()?
(внёс вклад brian d foy)
Вызов подпрограммы как &foo без последующих скобок игнорирует прототип foo и передает ей текущее значение списка аргументов, @_. Вот пример; подпрограмма bar вызывает подпрограмму &foo, которая выводит список своих аргументов:
sub foo { print "Args in foo are: @_\n"; }
sub bar { &foo; }
bar( "a", "b", "c" ); Когда вы вызываете bar с аргументами, вы видите, что foo получила тот же @_:
Args in foo are: a b c Вызов подпрограммы со следующими скобками, с аргументами или без них, не использует текущий @_. Изменение примера, чтобы поместить скобки после вызова foo, изменяет программу:
sub foo { print "Args in foo are: @_\n"; }
sub bar { &foo(); }
bar( "a", "b", "c" ); Теперь вывод показывает, что foo не получает @_ от своего вызывающего элемента.
Args in foo are: Однако, использование & в вызове всё ещё переопределяет прототип foo при его наличии:
sub foo ($$$) { print "Args infoo are: @_\n"; }
sub bar_1 { &foo; }
sub bar_2 { &foo(); }
sub bar_3 { foo( $_[0], $_[1], $_[2] ); }
# sub bar_4 { foo(); }
# bar_4 doesn't compile: "Not enough arguments for main::foo at ..."
bar_1( "a", "b", "c" );
# Args in foo are: a b c
bar_2( "a", "b", "c" );
# Args in foo are:
bar_3( "a", "b", "c" );
# Args in foo are: a b c Основное применение функции @_ — создание подпрограмм, основная задача которых — вызывать за вас другие подпрограммы. Для получения дополнительной информации см. perlsub.
Как создать оператор switch или case?
В Perl 5.10 используйте конструкцию given-when, описанную в perlsyn:
use 5.010;
given ( $string ) {
when( 'Fred' ) { say "I found Fred!" }
when( 'Barney' ) { say "I found Barney!" }
when( /Bamm-?Bamm/ ) { say "I found Bamm-Bamm!" }
default { say "I don't recognize the name!" }
}; Если нужно использовать чистый Perl и быть совместимым с версиями Perl до 5.10, общий ответ — использовать if-elsif-else:
for ($variable_to_test) {
if (/pat1/) { } # do something
elsif (/pat2/) { } # do something else
elsif (/pat3/) { } # do something else
else { } # default
} Вот простой пример оператора switch на основе сопоставления с образцом, представленный в структуре, напоминающей оператор switch. Мы выполним многостороннее условное выражение, основанное на типе ссылки, хранящейся в $whatchamacallit:
SWITCH: for (ref $whatchamacallit) {
/^$/ && die "not a reference";
/SCALAR/ && do {
print_scalar($$ref);
last SWITCH;
};
/ARRAY/ && do {
print_array(@$ref);
last SWITCH;
};
/HASH/ && do {
print_hash(%$ref);
last SWITCH;
};
/CODE/ && do {
warn "can't print function ref";
last SWITCH;
};
# DEFAULT
warn "User defined type skipped";
} См. perlsyn для других примеров в этом стиле.
Иногда следует менять местами константу и переменную. Например, предположим, что вам нужно определить, какой из нескольких ответов вы получили, но с учётом регистронезависимого сопоставления, которое также позволяет использовать сокращения. Вы можете использовать следующий метод, если строки все начинаются с разных символов или если вы хотите указать порядок совпадений так, что один из них имеет приоритет над другим, так как "SEND" имеет приоритет над "STOP" здесь:
chomp($answer = <>);
if ("SEND" =~ /^\Q$answer/i) { print "Action is send\n" }
elsif ("STOP" =~ /^\Q$answer/i) { print "Action is stop\n" }
elsif ("ABORT" =~ /^\Q$answer/i) { print "Action is abort\n" }
elsif ("LIST" =~ /^\Q$answer/i) { print "Action is list\n" }
elsif ("EDIT" =~ /^\Q$answer/i) { print "Action is edit\n" } Полностью другой подход — создание хеша с ссылками на функции.
my %commands = (
"happy" => \&joy,
"sad", => \&sullen,
"done" => sub { die "See ya!" },
"mad" => \&angry,
);
print "How are you? ";
chomp($string = <STDIN>);
if ($commands{$string}) {
$commands{$string}->();
} else {
print "No such command: $string\n";
} Начиная с Perl 5.8, модуль фильтра исходного кода Switch также можно использовать для получения операторов switch и case. Его использование теперь не рекомендуется, поскольку он не полностью совместим с родным оператором switch Perl 5.10 и потому что, будучи реализованным как фильтр исходного кода, он не всегда работает так, как предполагалось, при использовании сложной синтаксической конструкции.
Как перехватить доступ к неопределённым переменным, функциям или методам?
Метод AUTOLOAD, обсуждаемый в "Автозагрузка" в perlsub, позволяет перехватывать вызовы неопределенных функций и методов.
В случае неопределенных переменных, которые вызывают предупреждение в use warnings, вы можете преобразовать предупреждение в ошибку.
use warnings FATAL => qw(uninitialized); Почему не удаётся найти метод, включённый в тот же файл?
Возможные причины: проблема с наследованием, неправильное написание имени метода или неправильный тип объекта. Подробнее о каждом из этих случаев смотрите в perlootut. Вы также можете использовать print ref($object) для определения класса, в который $object был благословлён.
Другая возможная причина проблем — использование синтаксиса косвенного объекта (например, find Guru "Samy") для имени класса до того, как Perl обнаружил существование такого пакета. Лучше всего убедиться, что все ваши пакеты определены до начала их использования, что будет сделано, если вы используете инструкцию use вместо require. В противном случае убедитесь, что вы используете стрелочную нотацию (например, Guru->find("Samy")) вместо неё. Нотация объекта описана в perlobj.
Убедитесь, что вы прочитали информацию о создании модулей в perlmod и о проблемах с косвенными объектами в "Вызов метода" в perlobj.
Как узнать свой текущий или вызывающий пакет?
(внёс вклад brian d foy)
Чтобы узнать пакет, в котором вы сейчас находитесь, используйте специальный литерал __PACKAGE__, как документировано в perldata. Вы можете использовать специальные литералы только как отдельные токены, поэтому вы не можете интерполировать их в строки, как вы это делаете с переменными:
my $current_package = __PACKAGE__;
print "I am in package $current_package\n"; Если нужно определить пакет, вызывающий ваш код, возможно, для улучшения диагностики, как это делает Carp, используйте встроенную функцию caller:
sub foo {
my @args = ...;
my( $package, $filename, $line ) = caller;
print "I was called from package $package\n";
); По умолчанию ваша программа начинается в пакете main, поэтому вы всегда находитесь в каком-то пакете.
Это отличается от определения пакета, в который благословлён объект, который может не быть текущим пакетом. Для этого используйте blessed из Scalar::Util, который входит в стандартную библиотеку с Perl 5.8:
use Scalar::Util qw(blessed);
my $object_package = blessed( $object ); В большинстве случаев вы не должны заботиться о том, в какой пакет благословлён объект, но пока он утверждает, что наследуется от этого класса:
my $is_right_class = eval { $object->isa( $package ) }; # true or false И, с Perl 5.10 и более поздними версиями, вам не нужно проверять наследование, чтобы увидеть, может ли объект обработать роль. Для этого вы можете использовать DOES, который происходит из UNIVERSAL:
my $class_does_it = eval { $object->DOES( $role ) }; # true or false Вы можете безопасно заменить isa на DOES (хотя обратное неверно).
Как закомментировать большой блок кода Perl?
(внёс вклад brian d foy)
Быстрый и грязный способ закомментировать более одной строки Perl — это окружение этих строк директивами Pod. Эти директивы должны быть в начале строки и там, где Perl ожидает новую инструкцию (то есть не в середине инструкций, как в # комментариях). Вы завершаете комментарий с помощью =cut, завершая раздел Pod:
=pod
my $object = NotGonnaHappen->new();
ignored_sub();
$wont_be_assigned = 37;
=cut Быстрый и грязный метод хорошо работает только тогда, когда вы не планируете оставлять закомментированный код в источнике. Если появится обработчик Pod, ваш многострочный комментарий будет отображаться в переводе Pod. Лучший способ скрывает его от парсеров Pod.
Директива =begin может пометить раздел для определённой цели. Если парсер Pod не хочет его обрабатывать, он просто его игнорирует. Пометьте комментарии comment. Завершите комментарий с помощью =end с той же меткой. Вы по-прежнему нуждаетесь в =cut для возвращения в код Perl из Pod-комментария:
=begin comment
my $object = NotGonnaHappen->new();
ignored_sub();
$wont_be_assigned = 37;
=end comment
=cut Дополнительную информацию о Pod см. в perlpod и perlpodspec.
Как очистить пакет?
Используйте этот код, предоставленный Марком-Джейсоном Доминиусом:
sub scrub_package {
no strict 'refs';
my $pack = shift;
die "Shouldn't delete main package"
if $pack eq "" || $pack eq "main";
my $stash = *{$pack . '::'}{HASH};
my $name;
foreach $name (keys %$stash) {
my $fullname = $pack . '::' . $name;
# Get rid of everything with that name.
undef $$fullname;
undef @$fullname;
undef %$fullname;
undef &$fullname;
undef *$fullname;
}
} Или, если вы используете недавнюю версию Perl, вы можете просто использовать функцию Symbol::delete_package() вместо неё.
Как использовать переменную в качестве имени переменной?
Начинающие часто думают, что им нужна переменная, содержащая имя переменной.
$fred = 23;
$varname = "fred";
++$$varname; # $fred now 24 Это работает иногда, но это очень плохая идея по двум причинам.
Первая причина в том, что этот метод только работает с глобальными переменными. Это означает, что если $fred — это лексическая переменная, созданная с помощью my() в приведённом выше примере, код не будет работать вообще: вы случайно получите доступ к глобальной переменной и пропустите частную лексическую переменную. Глобальные переменные плохи тем, что они могут легко случайно сталкиваться и в общем случае создают не масштабируемый и запутанный код.
Символьные ссылки запрещены в рамках псевдонима use strict. Они не являются истинными ссылками и, следовательно, не учитываются при подсчёте ссылок или сборке мусора.
Другая причина, по которой использование переменной для хранения имени другой переменной – плохая идея, заключается в том, что вопрос часто возникает из-за недостаточного понимания перловских структур данных, особенно хешей. Используя символьные ссылки, вы просто используете хеш таблицы символов пакета (например, %main::) вместо пользовательского хеша. Решение состоит в использовании собственного хеша или реальной ссылки.
$USER_VARS{"fred"} = 23;
my $varname = "fred";
$USER_VARS{$varname}++; # not $$varname++ Там мы используем хеш %USER_VARS вместо символьных ссылок. Иногда это возникает при чтении строк от пользователя со ссылками на переменные и желании расширить их до значений переменных вашей программы Perl. Это также плохая идея, так как она объединяет адресное пространство программы и адресное пространство пользователя. Вместо того, чтобы читать строку и расширять её до фактического содержимого переменных вашей программы:
$str = 'this has a $fred and $barney in it';
$str =~ s/(\$\w+)/$1/eeg; # need double eval лучше хранить хеш, например, %USER_VARS, и иметь ссылки на переменные, фактически ссылающиеся на записи в этом хеше:
$str =~ s/\$(\w+)/$USER_VARS{$1}/g; # no /e here at all Это быстрее, чище и безопаснее, чем предыдущий подход. Конечно, вам не обязательно использовать знак доллара. Вы можете использовать свою собственную схему, чтобы сделать её менее запутанной, например, с квадратными скобками и знаками процента.
$str = 'this has a %fred% and %barney% in it';
$str =~ s/%(\w+)%/$USER_VARS{$1}/g; # no /e here at all Ещё одна причина, по которой люди иногда думают, что им нужна переменная, содержащая имя переменной, заключается в том, что они не знают, как создавать правильные структуры данных с помощью хешей. Например, предположим, что в их программе есть два хеша: %fred и %barney, и они хотели использовать другую скалярную переменную для ссылки на них по имени.
$name = "fred";
$$name{WIFE} = "wilma"; # set %fred
$name = "barney";
$$name{WIFE} = "betty"; # set %barney Это всё ещё символическая ссылка и по-прежнему имеет те же проблемы, что перечислены выше. Гораздо лучше написать:
$folks{"fred"}{WIFE} = "wilma";
$folks{"barney"}{WIFE} = "betty"; И просто начать с многоуровневого хеша.
Единственные случаи, когда вам обязательно нужно использовать символьные ссылки, это когда вам действительно нужно обратиться к таблице символов. Это может быть потому, что это что-то, на что нельзя получить реальную ссылку, например, имя формата. Это также может быть важно для вызовов методов, так как они всегда проходят через таблицу символов для разрешения.
В этих случаях вы временно отключите strict 'refs', чтобы поиграть с таблицей символов. Например:
@colors = qw(red blue green yellow orange purple violet);
for my $name (@colors) {
no strict 'refs'; # renege for the block
*$name = sub { "<FONT COLOR='$name'>@_</FONT>" };
} Все эти функции (red(), blue(), green() и т. д.) кажутся отдельными, но фактический код в замыкании был скомпилирован только один раз.
Таким образом, иногда вам может потребоваться использовать символьные ссылки для непосредственного управления таблицей символов. Это не важно для форматов, дескрипторов и подпрограмм, потому что они всегда глобальные – вы не можете использовать my() для них. Для скаляров, массивов и хешей, а также обычно для подпрограмм, вы, вероятно, захотите использовать только жёсткие ссылки.
Что означает «плохой интерпретатор»?
(представлено Brian d foy)
Сообщение «плохой интерпретатор» поступает от оболочки, а не от Perl. Фактическое сообщение может варьироваться в зависимости от вашей платформы, оболочки и настроек локали.
Если вы видите «плохой интерпретатор – такого файла или каталога не существует», первая строка в вашем скрипте Perl (строка «шебанга») не содержит правильного пути к Perl (или любой другой программе, способной запускать скрипты). Иногда это происходит, когда вы перемещаете скрипт с одной машины на другую, и каждая машина имеет разные пути к Perl – /usr/bin/perl по сравнению с /usr/local/bin/perl, например. Это также может указывать на то, что на исходной машине используются символы конца строки CRLF, а на целевой – только LF: оболочка пытается найти /usr/bin/perl
Если вы видите «плохой интерпретатор: Разрешение отказано», вам нужно сделать свой скрипт исполняемым.
В любом случае, вы по-прежнему можете запускать скрипты с помощью Perl явно:
% perl script.pl Если вы видите сообщение вроде «perl: команда не найдена», Perl не указан в вашей переменной среды PATH, что также может означать, что расположение Perl не такое, как вы ожидаете, поэтому вам нужно скорректировать строку шебанга.
Нужно ли перекомпилировать модули XS при изменении библиотеки C?
(представлено Alex Beamish)
Если новая версия библиотеки C совместима по ABI (это совместимость интерфейса двоичного приложения) с той, которую вы обновляете, и если версия общей библиотеки не изменилась, перекомпиляция не потребуется.
АВТОР И АВТОРСКИЕ ПРАВА
Авторские права (c) 1997-2013 Tom Christiansen, Nathan Torkington и другие авторы, как указано. Все права защищены.
Данная документация бесплатна; вы можете распространять и/или изменять её на тех же условиях, что и Perl сам.
Независимо от его распространения, все примеры кода в этом файле объявляются общественным достоянием. Вы можете использовать этот код в своих программах для развлечения или с целью получения прибыли, как вам заблагорассудится. Простой комментарий в коде, дающий почёт автору, был бы вежливым, но не обязательным.
© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.28.3/perlfaq7