Spec-Zone.ru › Perl 5.32

perlfaq7

СОДЕРЖАНИЕ

  • НАЗВАНИЕ
  • ВЕРСИЯ
  • ОПИСАНИЕ
    • Можно ли получить BNF/yacc/RE для языка Perl?
    • Какие это все знаки препинания $@%&*, и как узнать, когда их использовать?
    • Всегда ли/никогда не нужно заключать мои строки в кавычки или использовать точки с запятой и запятые?
    • Как пропустить некоторые возвращаемые значения?
    • Как временно заблокировать предупреждения?
    • Что такое расширение?
    • Почему операторы Perl имеют разный приоритет, чем операторы C?
    • Как объявить/создать структуру?
    • Как создать модуль?
    • Как принять или взять на себя модуль, уже находящийся на CPAN?
    • Как создать класс?
    • Как узнать, является ли переменная зараженной?
    • Что такое замыкание?
    • Что такое самоубийство переменных и как его предотвратить?
    • Как передать/вернуть {Функцию, дескриптор файла, массив, хеш, метод, регулярное выражение}?
    • Как создать статическую переменную?
    • В чем разница между динамическим и лексическим (статическим) охватом? Между local() и my()?
    • Как получить доступ к динамической переменной, в то время как лексическая переменная с похожим именем находится в области видимости?
    • В чем разница между глубокой и поверхностной связью?
    • Почему "my($foo) = <$fh>;" не работает правильно?
    • Как переопределить встроенную функцию, оператор или метод?
    • В чем разница между вызовом функции как &foo и foo()?
    • Как создать оператор switch или case?
    • Как перехватить обращения к неопределенным переменным, функциям или методам?
    • Почему не удается найти метод, включенный в тот же файл?
    • Как узнать мой текущий или вызывающий пакет?
    • Как закомментировать большой блок кода Perl?
    • Как очистить пакет?
    • Как использовать переменную как имя переменной?
    • Что означает "плохой интерпретатор"?
    • Нужно ли перекомпилировать XS-модули при изменении библиотеки C?
  • АВТОР И АВТОРСКИЕ ПРАВА

НАЗВАНИЕ

perlfaq7 - Общие вопросы по языку Perl

ВЕРСИЯ

версия 5.20200523

ОПИСАНИЕ

Этот раздел посвящен общим проблемам языка 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 или более позднюю версию, 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. ;)

«Самоубийство переменной» — это когда вы (временно или постоянно) теряете значение переменной. Это происходит из-за взаимодействия scope-модификаторов 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. Это "typeglobs" — см. "Typeglobs и дескрипторы файлов" в 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, например + или **, то вам понадобится pragma 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", первая строка в вашем перл-скрипте (строка shebang) не содержит правильный путь к Perl (или любой другой программе, способной запускать скрипты). Иногда это происходит, когда вы перемещаете скрипт с одной машины на другую, и каждая машина имеет другой путь к Perl — /usr/bin/perl по сравнению с /usr/local/bin/perl, например. Это также может указывать на то, что на исходной машине используются CRLF-окончания строк, а на целевой — только LF: оболочка пытается найти /usr/bin/perl, но не может.

Если вы видите сообщение "bad interpreter: Permission denied", вам нужно сделать ваш скрипт исполняемым.

В любом случае, вы по-прежнему можете запустить скрипты с помощью Perl явно:

% perl script.pl

Если вы получаете сообщение вроде "perl: command not found", Perl не указан в вашем PATH, что также может означать, что расположение Perl не то, которое вы ожидаете, поэтому вам нужно настроить строку shebang.

Нужно ли перекомпилировать XS-модули при изменении C-библиотеки?

(внесено Alex Beamish)

Если новая версия C-библиотеки совместима по ABI (Application Binary Interface) с той, с которой вы обновляетесь, и если версия разделяемой библиотеки не изменилась, перекомпиляция не потребуется.

АВТОР И АВТОРСКИЕ ПРАВА

Авторские права (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.32.0/perlfaq7

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API