Обычные вещи для 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, и мы получили неожиданный результат.