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.20210411
ОПИСАНИЕ
Этот раздел посвящён общим вопросам по языку 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 в скалярном контексте или все строки в списочном контексте. При выполнении операций open, close или любых других операций, кроме <> с файлами, или даже при упоминании дескриптора, не используйте скобки. Вот правильные варианты: 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 отрицательной четвёркой, а не положительной единицей. Он также правоассоциативен, означая, что 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 Обратите внимание, что значение «приватный» ни разу не выводится. Это потому, что $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 есть оператор 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 на основе сопоставления с образцом, организованный так, чтобы выглядеть как оператор 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.
Как очистить пакет?
Используйте этот код, предоставленный Mark-Jason Dominus:
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 вместо символьных ссылок. Иногда это возникает при чтении строк от пользователя со ссылками на переменные и желании расширить их до значений переменных вашей перловской программы. Это также плохая идея, так как она смешивает пространство имен, доступное программе, и пространство имен, доступное пользователю. Вместо того, чтобы читать строку и расширять её до фактического содержимого переменных вашей программы:
$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() для них. Однако для скаляров, массивов и хешей, а также обычно для подпрограмм, вы, вероятно, захотите использовать жёсткие ссылки.
Что означает сообщение «bad interpreter»?
(предложено brian d foy)
Сообщение «bad interpreter» исходит от оболочки, а не от Perl. Фактическое сообщение может варьироваться в зависимости от вашей платформы, оболочки и параметров локализации.
Если вы видите сообщение «bad interpreter - no such file or directory», первая строка в вашем скрипте Perl (строка «shebang») не содержит правильного пути к Perl (или любой другой программе, способной выполнять скрипты). Иногда это происходит, когда вы перемещаете скрипт с одной машины на другую, и каждая машина имеет другой путь к Perl — /usr/bin/perl по сравнению с /usr/local/bin/perl, например. Это также может указывать на то, что исходная машина использует символы конца строки CRLF, а целевая машина — только LF: оболочка пытается найти /usr/bin/perl<CR>, но не может.
Если вы видите сообщение «bad interpreter: Permission denied», вам нужно сделать ваш скрипт исполняемым.
В любом случае вы по-прежнему можете запустить скрипты с явным указанием perl:
% perl script.pl Если вы получаете сообщение «perl: command not found», Perl не находится в вашем PATH, что также может означать, что местоположение Perl не то, что вы ожидаете, поэтому вам нужно скорректировать строку shebang.
Нужно ли перекомпилировать модули XS при изменении библиотеки C?
(предложено Alex Beamish)
Если новая версия библиотеки C совместима по ABI (это совместимость интерфейса двоичного кода) с той версией, с которой вы обновляетесь, и если версия разделяемой библиотеки не изменилась, перекомпиляция не потребуется.
АВТОР И АВТОРСКИЕ ПРАВА
Авторские права (c) 1997-2013 Tom Christiansen, Nathan Torkington и другие авторы, как указано. Все права защищены.
Данная документация распространяется свободно; вы можете перераспределять её и/или изменять её на тех же условиях, что и Perl сам по себе.
Независимо от способа распространения, все примеры кода в этом файле объявляются общественным достоянием. Вы можете и должны использовать этот код в своих программах для развлечения или получения прибыли, как вам заблагорассудится. Небольшое упоминание в коде с указанием авторства будет вежливым, но не обязательным.
© 1993–2021 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.34.0/perlfaq7