perlfaq7
СОДЕРЖАНИЕ
- НАЗВАНИЕ
- ВЕРСИЯ
- ОПИСАНИЕ
- Можно ли получить BNF/yacc/RE для языка Perl?
- Какие это все знаки препинания $@%&*, и как узнать, когда их использовать?
- Всегда ли/никогда ли мне нужно заключать в кавычки мои строки или использовать точки с запятой и запятые?
- Как пропустить некоторые возвращаемые значения?
- Как временно заблокировать предупреждения?
- Что такое расширение?
- Почему операторы Perl имеют разный приоритет по сравнению с операторами C?
- Как объявить/создать структуру?
- Как создать модуль?
- Как принять или перехватить модуль, уже находящийся на CPAN?
- Как создать класс?
- Как узнать, если переменная заражена?
- Что такое замыкание?
- Что такое самоубийство переменной и как его предотвратить?
- Как передать/вернуть {Функцию, Дескриптор файла, Массив, Хэш, Метод, Регулярное выражение}?
- Как создать статическую переменную?
- В чем разница между динамическим и лексическим (статическим) охватом? Между local() и my()?
- Как получить доступ к динамической переменной, пока аналогично названная лексическая переменная находится в области видимости?
- В чем разница между глубокой и поверхностной привязкой?
- Почему "my($foo) = <$fh>;" работает неправильно?
- Как переопределить встроенную функцию, оператор или метод?
- В чем разница между вызовом функции как &foo и foo()?
- Как создать оператор switch или case?
- Как поймать обращения к неопределенным переменным, функциям или методам?
- Почему не удается найти метод, включенный в этот же файл?
- Как узнать свой текущий или вызывающий пакет?
- Как закомментировать большой блок кода Perl?
- Как очистить пакет?
- Как использовать переменную как имя переменной?
- Что означает "плохой интерпретатор"?
- Нужно ли перекомпилировать XS-модули при изменении библиотеки C?
- АВТОР И АВТОРСКИЕ ПРАВА
НАЗВАНИЕ
perlfaq7 - Общие проблемы языка Perl
ВЕРСИЯ
версия 5.20210520
ОПИСАНИЕ
Этот раздел посвящен общим проблемам языка Perl, которые не подходят явно ни к одному из других разделов.
Можно ли получить BNF/yacc/RE для языка Perl?
BNF нет, но вы можете самостоятельно пройтись по грамматике yacc в perly.y в дистрибутиве исходных кодов, если вы особенно смелы. Грамматика опирается на очень умный код токенизации, поэтому будьте готовы к тому, что придется углубиться в toke.c тоже.
По словам Chaim Frenkel: «Грамматика 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 или более позднюю версию, 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 дать отрицательную четвёрку, а не положительную единицу. Он также является правоассоциативным, что означает, что 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.
END_OF_DOCUMENT_MARKERЕсли вам нужно включить код на C или интерфейсы библиотек C, используйте h2xs. h2xs создаст структуру распределения модуля и начальные файлы интерфейса. perlxs и perlxstut объясняют детали.
Как принять или взять на себя ответственность за модуль, уже находящийся на CPAN?
Попросите текущего администратора сделать вас соадминистратором или передать вам модуль.
Если по какой-то причине вы не можете связаться с автором, обратитесь к администраторам PAUSE по адресу modules@perl.org, которые могут помочь, но каждый случай рассматривается индивидуально.
-
Получите учетную запись на Perl Authors Upload Server (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 получила те же самые @_, что и у bar:
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 есть оператор given/when, но он экспериментальный и, вероятно, изменится в будущем. См. perlsyn для получения дополнительной информации.
В общем случае следует использовать модуль CPAN, такой как Switch::Plain:
use Switch::Plain;
sswitch($variable_holding_a_string) {
case 'first': { }
case 'second': { }
default: { }
} или для более сложных сравнений, if-elsif-else:
for ($variable_to_test) {
if (/pat1/) { } # do something
elsif (/pat2/) { } # do something else
elsif (/pat3/) { } # do something else
else { } # default
} Вот простой пример переключателя, основанного на сопоставлении шаблонов, выстроенный так, чтобы он выглядел как оператор 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. Они не являются истинными ссылками и, следовательно, не учитываются при подсчёте ссылок или при сборке мусора.
Вторая причина, по которой использование переменной для хранения имени другой переменной — плохая идея, заключается в том, что этот вопрос часто возникает из-за отсутствия понимания структур данных Perl, особенно хэшей. Используя символьные ссылки, вы просто используете хеш таблицы символов пакета (например, %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<CR>, но не может.
Если вы видите сообщение «плохой интерпретатор: права доступа запрещены», вам необходимо сделать свой скрипт исполняемым.
В любом случае вы по-прежнему можете запустить скрипты с помощью явного указания Perl:
% perl script.pl Если вы получаете сообщение «perl: команда не найдена», Perl не указан в переменной окружения PATH, что также может означать, что расположение Perl не такое, как вы ожидаете, поэтому вам необходимо изменить строку шебанг.
Нужно ли перекомпилировать модули XS при изменении библиотеки C?
(предоставлено Alex Beamish)
Если новая версия библиотеки C совместима по ABI (это означает совместимость по интерфейсу двоичного кода) с версией, с которой вы выполняете обновление, и если версия общей библиотеки не изменилась, перекомпиляция не потребуется.
АВТОР И АВТОРСКИЕ ПРАВА
Copyright (c) 1997-2013 Tom Christiansen, Nathan Torkington и другие авторы, как указано. Все права защищены.
Данная документация бесплатна; вы можете распространять и/или изменять её на тех же условиях, что и Perl сам по себе.
Независимо от способа распространения, все примеры кода в этом файле объявляются всемирным достоянием. Вам разрешается и рекомендуется использовать этот код в своих программах для развлечения или получения прибыли, как вам заблагорассудится. Простая пометка в коде с указанием авторства была бы вежливым жестом, но не является обязательной.
© 1993–2023 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.38.0/perlfaq7