Обычные вещи для Perl?
Вопрос о Скрытых чертах Perl дал как минимум один ответ, который можно было бы рассматривать как функции или неправильной функции. Казалось логичным следить за этим вопросом: каковы распространенные неочевидные ошибки в Perl? Кажется, что они должны работать, но не надо.
Я не буду давать рекомендации относительно того, как структурировать ответы, или что "слишком легко" можно считать "добычей", поскольку для этого важно голосование.
Таблица ответов
Синтаксис
Функции семантики/языка
- Общие
- Контекст
- Переменные
Отладка
Лучшие практики
Мета-ответы
См. также: ASP.NET - общие исправления
Ответы
Ответ 1
Тот факт, что одинарные кавычки могут использоваться для замены:: в идентификаторах.
Рассмотрим:
use strict;
print "$foo"; #-- Won't compile under use strict
print "$foo fun!"; #-- Compiles just fine, refers to $foo::s
Следуя следующей проблеме:
use strict;
my $name = "John";
print "$name name is '$name'";
# prints:
# name is 'John'
Рекомендуемый способ избежать этого - использовать фигурные скобки вокруг имени переменной:
print "${name} name is '$name'";
# John name is 'John'
А также в use warnings
, так как он расскажет вам об использовании переменной undefined $name::s
Ответ 2
Вы можете печатать на лексическом дескрипторе файла: хорошо.
print $out "hello, world\n";
Затем вы понимаете, что было бы неплохо иметь хэш дескрипторов файлов:
my %out;
open $out{ok}, '>', 'ok.txt' or die "Could not open ok.txt for output: $!";
open $out{fail}, '>', 'fail.txt' or die "Could not open fail.txt for output: $!";
До сих пор так хорошо. Теперь попробуйте использовать их и распечатайте в том или ином состоянии в соответствии с условием:
my $where = (frobnitz() == 10) ? 'ok' : 'fail';
print $out{$where} "it worked!\n"; # it didn't: compile time error
Вы должны обернуть различие хешей в паре завитушек:
print {$out{$where}} "it worked!\n"; # now it did
Это совершенно неинтуитивное поведение. Если вы не слышали об этом или читали его в документации, я сомневаюсь, что вы могли бы понять это самостоятельно.
Ответ 3
Это мета-ответ. Многие неприятные gotchas пойманы Perl::Critic, которые вы можете установить и запустить из командной строки с помощью perlcritic
или (если вы счастливы отправить свой код через Интернет и не сможете настроить свои параметры) с помощью Perl:: Critic website.
Perl::Critic
также содержит ссылки на книгу лучших практик Damian Conways Perl, включая номера страниц. Поэтому, если вы слишком ленивы, чтобы прочитать всю книгу, Perl::Critic
все равно может рассказать вам о битах, которые вы должны читать.
Ответ 4
Perl DWIMmer борется с записью <<
(here-document) при использовании print
с лексическими дескрипторами файла:
# here-doc
print $fh <<EOT;
foo
EOT
# here-doc, no interpolation
print $fh <<'EOT';
foo
EOT
# bitshift, syntax error
# Bareword "EOT" not allowed while "strict subs" in use
print $fh<<EOT;
foo
EOT
# bitshift, fatal error
# Argument "EOT" isn't numeric...
# Can't locate object method "foo" via package "EOT"...
print $fh<<'EOT';
foo
EOT
Решение состоит в том, чтобы быть осторожным, чтобы включить пробел между дескриптором файла и <<
или устранить ошибку дескриптора файла, завернув его в {}
скобки:
print {$fh}<<EOT;
foo
EOT
Ответ 5
perltrap manpage перечисляет много ловушек для неосторожного организованного типа.
Ответ 6
Наиболее распространенным способом является запуск файлов с чем-то другим, чем
use strict;
use diagnostics;
pjf добавляет: Пожалуйста, будьте осторожны, что диагностика оказывает значительное влияние на производительность. Он замедляет запуск программы, так как он должен загружать perldiag.pod, и до тех пор, пока несколько недель назад он не снизится, он также замедлит и раздувает регулярные выражения, поскольку использует $&. Рекомендуется использовать предупреждения и запуск splain
по результатам.
Ответ 7
Сбивание ссылок и фактических объектов:
$a = [1,2,3,4];
print $a[0];
(Он должен быть одним из $a->[0]
(лучший), $$a[0]
, @{$a}[0]
или @$a[0]
)
Ответ 8
Назначение массивов скалярам для меня не имеет смысла. Например:
$foo = ( 'a', 'b', 'c' );
Присваивает 'c' $foo и удаляет остальную часть массива. Это более странно:
@foo = ( 'a', 'b', 'c' );
$foo = @foo;
Похоже, что он должен делать то же, что и в первом примере, но вместо этого он устанавливает $foo
в длину @foo
, поэтому $foo == 3
.
Ответ 9
"мои" объявления должны использовать круглые скобки вокруг списков переменных
use strict;
my $a = 1;
mysub();
print "a is $a\n";
sub {
my $b, $a; # Gotcha!
$a = 2;
}
Он печатает a is 2, потому что объявление my
применяется только к $b
(упоминание $a
на этой строке просто ничего не делало). Обратите внимание, что это происходит без предупреждения, даже когда действует "use strict".
Добавление "предупреждений об использовании" (или флаг -w) значительно улучшает работу с Perl, говорящей, что круглые скобки отсутствуют в "моем" списке. Это показывает, как многие уже знают, почему и правильные, и прагматические предупреждения всегда являются хорошей идеей.
Ответ 10
Использование неинициализированного значения в конкатенации...
Это сводит меня с ума. У вас есть печать, которая включает в себя ряд переменных, например:
print "$label: $field1, $field2, $field3\n";
И одна из переменных - undef. Вы считаете это ошибкой в своей программе - почему вы использовали "строгую" прагму. Возможно, ваша схема базы данных разрешила NULL в поле, которое вы не ожидали, или вы забыли инициализировать переменную и т.д. Но все сообщение об ошибке сообщает вам, что во время операции конкатенации (.
) было обнаружено неинициализированное значение. Если бы он сказал вам имя переменной, которая была неинициализирована!
Поскольку Perl не хочет печатать имя переменной в сообщении об ошибке по какой-либо причине, вы в конечном итоге отслеживаете ее, установив точку останова (чтобы посмотреть, какая переменная undef), или добавление кода для проверки состояния. Очень раздражает, когда это происходит только один раз из тысяч в CGI script, и вы не можете легко воссоздать его.
Ответ 11
Большинство операторов цикла Perl (foreach
, map
, grep
) автоматически локализуют $_
, но while(<FH>)
нет. Это может привести к странным действиям на расстоянии.
Ответ 12
my $x = <>;
do {
next if $x !~ /TODO\s*[:-]/;
...
} while ( $x );
do
не является циклом. Вы не можете next
. Это инструкция для выполнения блока. Это то же самое, что
$inc++ while <>;
Несмотря на то, что он похож на конструкцию в семействе языков C.
Ответ 13
Я сделал это один раз:
my $object = new Some::Random::Class->new;
Пришло время найти ошибку. Непрямым синтаксисом метода является eeevil.
Ответ 14
Константы могут быть переопределены. Простым способом случайного переопределения константы является определение константы как ссылки.
use constant FOO => { bar => 1 };
...
my $hash = FOO;
...
$hash->{bar} = 2;
Теперь FOO - {bar = > 2};
Если вы используете mod_perl (по крайней мере, в 1.3), новое значение FOO будет сохраняться до обновления модуля.
Ответ 15
Это исправлено в Perl 5.10 - если вам посчастливилось работать где-то, у которого нет аллергии на обновление вещей > : - (
Я говорю о Переменной, которая действительно имеет нулевое значение. Вы знаете, тот, который вызывает неожиданные результаты в таких предложениях, как:
unless ($x) { ... }
$x ||= do { ... };
Perl 5.10 имеет оператор // = или определенный или.
Это особенно коварно, когда действительный ноль вызван некоторым краевым условием, которое не рассматривалось при тестировании до того, как ваш код перешел к производству...
Ответ 16
Какие значения вы могли бы ожидать @_ для следующего сценария?
sub foo { }
# empty subroutine called in parameters
bar( foo(), "The second parameter." ) ;
Я ожидаю получить в баре:
undef, "The second parameter."
Но @_ содержит только второй параметр, по крайней мере, при тестировании с помощью perl 5.88.
Ответ 17
Унарный минус с "foo"
создает "-foo"
:
perl -le 'print -"foo" eq "-foo" ? "true" : "false"'
Это работает только в том случае, если первый символ соответствует /[_a-zA-Z]/
. Если первый символ является "-"
, то он меняет первый символ на "+"
, а если первый символ - "+"
, то он меняет первый символ на "-"
. Если первый символ соответствует /[^-+_a-zA-Z]/
, он пытается преобразовать строку в число и отрицает результат.
perl -le '
print -"foo";
print -"-foo";
print -"+foo";
print -"\x{e9}"; #e acute is not in the accepted range
print -"5foo"; #same thing for 5
'
Вышеприведенный код
-foo
+foo
-foo
-0
-5
Эта функция в основном существует, чтобы позволить людям говорить такие вещи, как
my %options = (
-depth => 5,
-width => 2,
-height => 3,
);
Ответ 18
Ответ Graeme Perrow был хорошим, но он становится еще лучше!
Учитывая типичную функцию, которая возвращает красивый список в контексте списка, вы можете спросить: что он вернет в скалярном контексте? (По "типичному", я имею в виду общий случай, когда документация не говорит, и мы предполагаем, что он не использует какой-либо смешной бизнес wantarray
. Может быть, это функция, которую вы написали сами.)
sub f { return ('a', 'b', 'c'); }
sub g { my @x = ('a', 'b', 'c'); return @x; }
my $x = f(); # $x is now 'c'
my $y = g(); # $y is now 3
Контекст, вызываемый функцией, распространяется на операторы return
в этой функции.
Я предполагаю, что вызывающий абонент ошибался, чтобы получить простое эмпирическое правило, позволяющее эффективно рассуждать о поведении кода. Вы правы, Perl, это лучше для персонажа вызывающего абонента, который каждый раз сталкивается с вызываемым функциональным кодом.
Ответ 19
Вы не можете локализовать экспортированные переменные, если вы не экспортируете весь типglob.
Ответ 20
Использование модификатора /o
с шаблоном регулярного выражения, хранящимся в переменной.
m/$pattern/o
Задание /o
является обещанием, что $pattern
не изменится. Perl достаточно умен, чтобы узнать, изменилось ли оно или нет, и повторно перекомпилировать регулярное выражение, поэтому нет веских оснований для использования /o
. В качестве альтернативы вы можете использовать qr//
(например, если вы одержимы тем, что избегаете проверки).
Ответ 21
Сравнение строк с использованием ==
и !=
вместо eq
и ne
. Например:
$x = "abc";
if ($x == "abc") {
# do something
}
Вместо:
$x = "abc";
if ($x eq "abc") {
# do something
}
Ответ 22
Как насчет того, что
@array = split( / /, $string );
не дает тот же результат, что и
@array = split( ' ', $string );
если $string имеет ведущие пробелы?
Это может занять некоторых людей врасплох.
Ответ 23
Добавление дополнительных круглых скобок никогда не может изменить значение кода, правильно? Правильно?
my @x = ( "A" x 5 ); # @x contains 1 element, "AAAAA"
my @y = (("A") x 5 ); # @y contains 5 elements: "A", "A", "A", "A", "A"
О, это правильно, это Perl.
EDIT: Просто для хорошей меры, если x
вызывается в скалярном контексте, то круглые скобки не имеют значения:
my $z = ( "A" x 5 ); # $z contains "AAAAA"
my $w = (("A") x 5 ); # $w contains "AAAAA" too
Интуитивный.
Ответ 24
Если вы достаточно глупы, чтобы сделать это, Perl позволит вам объявлять несколько переменных с тем же именем:
my ($x, @x, %x);
Поскольку Perl использует сигилы для определения контекста, а не типа переменной, это почти гарантирует путаницу, когда более поздний код использует переменные, особенно если $x
является ссылкой:
$x[0]
$x{key}
$x->[0]
$x->{key}
@x[0,1]
@x{'foo', 'bar'}
@$x[0,1]
@$x{'foo', 'bar'}
...
Ответ 25
Забыв перед отправкой пути к результатам readdir
, прежде чем выполнять тесты по этим результатам. Вот пример:
#!/usr/bin/env perl
use strict;
use warnings;
opendir my $dh, '/path/to/directory/of/interest'
or die "Can't open '/path/to/directory/of/interest for reading: [$!]";
my @files = readdir $dh; # Bad in many cases; see below
# my @files = map { "/path/to/directory/of/interest/$_" } readdir $dh;
closedir $dh or die "Can't close /path/to/directory/of/interest: [$!]";
for my $item (@files) {
print "File: $item\n" if -f $item;
# Nothing happens. No files? That odd...
}
# Scratching head...let see...
use Data::Dumper;
print Dumper @files;
# Whoops, there it is...
Этот заголовок упоминается в документации для readdir
, но я думаю, что это все еще довольно распространенная ошибка.
Ответ 26
Хэш-конструктор - это не что иное, как список, а жирная запятая =>
- не что иное, как синтаксический сахар. Вы можете укусить это, запутав синтаксис []
arrayref с синтаксисом списка ()
:
my %var = (
("bar", "baz"),
fred => "barney",
foo => (42, 95, 22)
);
# result
{ 'bar' => 'baz',
'95' => 22,
'foo' => 42,
'fred' => 'barney' };
# wanted
{ 'foo' => [ 42, 95, 22 ] }
Ответ 27
Имена пропущенных имен... Я когда-то провел целый день кода устранения неполадок, который не вел себя правильно, только чтобы найти опечатку на имя переменной, что не является ошибкой в Perl, а скорее объявление новой переменной.
Ответ 28
Изменение массива, в котором вы зацикливаетесь в for (каждый), как в:
my @array = qw/a b c d e f g h/;
for ( @array ) {
my $val = shift @array;
print $val, "\n";
}
он запутывается и не делает того, что вы ожидаете
Ответ 29
Обработка скаляра как целого:
$n = 1729;
$s = 0;
$i = 0;
while ($n) {
$s += $n % 10;
$n/=10;
$i ++
}
print "Sum : $s\n";
print "Number of iterations : $i\n"
Сумма: 19
Число итераций: 327
В идеале он должен был иметь только четыре итерации, но скаляр не является int, и мы получили неожиданный результат.