Spec-Zone.ru › Perl 5.30

perlfaq7

СОДЕРЖАНИЕ

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

НАЗВАНИЕ

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

ВЕРСИЯ

версия 5.20190126

ОПИСАНИЕ

Этот раздел посвящен общим вопросам по языку Perl, которые не ясно относятся ни к одному из других разделов.

Можно ли получить BNF/yacc/RE для языка Perl?

BNF нет, но вы можете попытаться разобраться в грамматике yacc в perly.y в дистрибутиве исходных кодов, если у вас достаточно смелости. Грамматика опирается на очень умный код токенизации, поэтому будьте готовы также углубиться в toke.c.

Словами Хаима Френкеля: «грамматика Perl не может быть сведена к BNF. Работа по разбору Perl распределена между yacc, лексером, дымом и зеркалами».

Какие это все знаки препинания $@%&* и как узнать, когда их использовать?

Это спецификаторы типов, как подробно описано в perldata:

$ for scalar values (number, string or reference)
@ for arrays
% for hashes (associative arrays)
& for subroutines (aka functions, procedures, methods)
* for all types of that symbol name. In version 4 you used them like
  pointers, but in modern perls you can just use references.

Есть несколько других символов, с которыми вы, вероятно, столкнетесь, которые не являются спецификаторами типов:

<> are used for inputting a record from a filehandle.
\  takes a reference to something.

Обратите внимание, что <FILE> — не спецификатор типа для файлов и не имя дескриптора. Это <> оператор, применённый к дескриптору FILE. Он считывает одну строку (точнее, запись — см. "$/" в perlvar) из дескриптора FILE в контексте скаляра или все строки в контексте списка. При выполнении операций открытие, закрытие или любых других операций, кроме <> с файлами, или даже при упоминании дескриптора, не используйте скобки. Правильно: eof(FH), seek(FH, 0, 2) и «копирование со STDIN на FILE».

Всегда ли нужно заключать в кавычки мои строки или использовать точки с запятой и запятые?

Обычно, для простого слова нет необходимости в кавычках, но в большинстве случаев, вероятно, следует их использовать (и это необходимо в use strict). Но ключевое слово хеша, состоящее из простого слова, и левая часть оператора => считаются как будто заключёнными в кавычки:

This                    is like this
------------            ---------------
$foo{line}              $foo{'line'}
bar => stuff            'bar' => stuff

Окончательная точка с запятой в блоке необязательна, как и заключительная запятая в списке. Хороший стиль (см. perlstyle) предлагает их использовать, за исключением однострочных выражений:

if ($whoops) { exit 1 }
my @nums = (1, 2, 3);

if ($whoops) {
    exit 1;
}

my @lines = (
    "There Beren came from mountains cold",
    "And lost he wandered under leaves",
);

Как пропустить некоторые возвращаемые значения?

Один способ — рассматривать возвращаемые значения как список и индексировать по нему:

$dir = (getpwnam($user))[7];

Другой способ — использовать undef в качестве элемента в левой части:

($dev, $ino, undef, undef, $uid, $gid) = stat($file);

Вы также можете использовать срез списка, чтобы выбрать только необходимые элементы:

($dev, $ino, $uid, $gid) = ( stat($file) )[0,1,4,5];

Как временно заблокировать предупреждения?

Если вы используете Perl 5.6.0 или более позднюю версию, pragma use warnings позволяет точно контролировать, какие предупреждения генерируются. См. perllexwarn для получения более подробной информации.

{
    no warnings;          # temporarily turn off warnings
    $x = $y + $z;         # I know these might be undef
}

Кроме того, вы можете включать и отключать категории предупреждений. Вы отключаете те категории, которые хотите проигнорировать, и всё равно можете получать предупреждения других категорий. См. perllexwarn для получения полной информации, включая имена категорий и их иерархию.

{
    no warnings 'uninitialized';
    $x = $y + $z;
}

Если у вас более старая версия Perl, переменная $^W (описанная в perlvar) управляет предупреждениями во время выполнения для блока:

{
    local $^W = 0;        # temporarily turn off warnings
    $x = $y + $z;         # I know these might be undef
}

Обратите внимание, что, как и все переменные-знаки препинания, вы не можете использовать my() на $^W, только local().

Что такое расширение?

Расширение — это способ вызова скомпилированного кода C из Perl. Чтение perlxstut — хорошее место для изучения расширений.

Почему операторы Perl имеют разный приоритет, чем операторы C?

На самом деле, нет. Все операторы C, которые копирует Perl, имеют тот же приоритет в Perl, что и в C. Проблема в операторах, которых нет в C, особенно функциях, которые дают контекст списка всему справа, например, print, chmod, exec и так далее. Такие функции называются «операторми списка», и они появляются как таковые в таблице приоритетов в perlop.

Частая ошибка — писать:

unlink $file || die "snafu";

Это интерпретируется как:

unlink ($file || die "snafu");

Чтобы избежать этой проблемы, либо поставьте дополнительные скобки, либо используйте оператор с очень низким приоритетом or:

(unlink $file) || die "snafu";
unlink $file or die "snafu";

Операторы «English» (and, or, xor, и not) преднамеренно имеют приоритет ниже, чем операторы списка, именно для таких ситуаций, как описанная выше.

Ещё один оператор с неожиданным приоритетом — возведение в степень. Он связывается более тесно, даже чем унарный минус, делая -2**2 отрицательным четырьмя, а не положительным единицей. Он также правоассоциативный, что означает, что 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 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. Это "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-оператор, например + или **, то вам нужно использовать прагму 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() с ними. Однако для скаляров, массивов и хешей, а также обычно для подпрограмм, вы, вероятно, хотите использовать только жёсткие ссылки.

Что означает сообщение "плохой интерпретатор"?

(представлено brian d foy)

Сообщение "плохой интерпретатор" поступает от оболочки, а не от Perl. Фактическое сообщение может варьироваться в зависимости от вашей платформы, оболочки и настроек локали.

Если вы видите "плохой интерпретатор — такого файла или каталога не существует", первая строка в вашем скрипте Perl (строка "shebang") не содержит правильного пути к Perl (или любой другой программе, способной запускать скрипты). Иногда это происходит, когда вы перемещаете скрипт с одной машины на другую, и на каждой машине есть разный путь к Perl — например, /usr/bin/perl по сравнению с /usr/local/bin/perl. Это также может указывать на то, что на машине-источнике используются символы конца строки CRLF, а на целевой машине — только LF: оболочка пытается найти /usr/bin/perl, но не может.

Если вы видите "плохой интерпретатор: права доступа запрещены", вам нужно сделать ваш скрипт исполняемым.

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

% perl script.pl

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

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

(представлено Alex Beamish)

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

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

Авторские права (c) 1997-2013 Tom Christiansen, Nathan Torkington и другие авторы, как указано. Все права защищены.

Данная документация бесплатна; вы можете распространять и/или изменять её на тех же условиях, что и Perl сам по себе.

Независимо от его распространения, все примеры кода в этом файле объявляются общественным достоянием. Вам разрешается и поощряется использовать этот код в своих программах для развлечения или получения прибыли по вашему усмотрению. Простой комментарий в коде с указанием авторства будет вежливым, но не является обязательным.

© 1993–2020 Larry Wall and others
Licensed under the GNU General Public License version 1 or later, or the Artistic License.
The Perl logo is a trademark of the Perl Foundation.
https://perldoc.perl.org/5.30.3/perlfaq7

Spec-Zone.ru

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