Общие ошибки для Perl?

71

Вопрос о Скрытых чертах Perl дал как минимум один ответ, который можно было бы рассматривать как функции или неправильной функции. Казалось логичным следить за этим вопросом: каковы распространенные неочевидные ошибки в Perl? Кажется, что они должны работать, но не надо.

Я не буду давать рекомендации относительно того, как структурировать ответы, или что "слишком легко" можно считать "добычей", поскольку для этого важно голосование.

Таблица ответов

Синтаксис

Функции семантики/языка

Отладка

Лучшие практики

Мета-ответы

См. также: ASP.NET - общие исправления

  • 0
    Не хочу просто публиковать ответ на другой вопрос, но, возможно, вы могли бы добавить его в свою таблицу ответов: stackoverflow.com/questions/146329/…
  • 0
    Может быть, кто-то может объяснить, почему это ошибка: stackoverflow.com/questions/163026/…
Теги:

29 ответов

36

Тот факт, что одинарные кавычки могут использоваться для замены:: в идентификаторах.

Рассмотрим:

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

  • 5
    Это, конечно, похмелье от Perl 4, которое было необходимо для обратной совместимости.
  • 1
    Вы правы, Джонатан, но пришло время разработать прагму для <b> включения </ b> этой так называемой функции. Пакетные методы Perl 4 похожи на латиницу. Поддержка их в этот день и возраст - это скорее гоча, чем выгода ИМХО.
Показать ещё 2 комментария
28

Вы можете печатать на лексическом дескрипторе файла: хорошо.

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

Это совершенно неинтуитивное поведение. Если вы не слышали об этом или читали его в документации, я сомневаюсь, что вы могли бы понять это самостоятельно.

  • 1
    Я также был укушен этим ... Как вы говорите, он не обнаружен.
  • 1
    print $out "text" можно перепутать с print $out, "text" . print {$out} "text" не будет.
27

Это мета-ответ. Многие неприятные gotchas пойманы Perl::Critic, которые вы можете установить и запустить из командной строки с помощью perlcritic или (если вы счастливы отправить свой код через Интернет и не сможете настроить свои параметры) с помощью Perl:: Critic website.

Perl::Critic также содержит ссылки на книгу лучших практик Damian Conways Perl, включая номера страниц. Поэтому, если вы слишком ленивы, чтобы прочитать всю книгу, Perl::Critic все равно может рассказать вам о битах, которые вы должны читать.

  • 1
    Для некоторого значения «следует», конечно.
  • 0
    Имеет ли Perl :: Critic оценку, например, 5 уровней?
18

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
17

perltrap manpage перечисляет много ловушек для неосторожного организованного типа.

15

Назначение массивов скалярам для меня не имеет смысла. Например:

$foo = ( 'a', 'b', 'c' );

Присваивает 'c' $foo и удаляет остальную часть массива. Это более странно:

@foo = ( 'a', 'b', 'c' );
$foo = @foo;

Похоже, что он должен делать то же, что и в первом примере, но вместо этого он устанавливает $foo в длину @foo, поэтому $foo == 3.

  • 7
    В первом примере у вас есть оператор запятой в скалярном контексте (там нет массива), так что вы получите последнее. Во втором примере у вас есть массив, поэтому вы получаете описанное вами поведение.
  • 2
    Другой способ сказать, что в первом примере у вас есть список, а во втором - массив.
Показать ещё 7 комментариев
15

Сбивание ссылок и фактических объектов:

$a = [1,2,3,4];
print $a[0];

(Он должен быть одним из $a->[0] (лучший), $$a[0], @{$a}[0] или @$a[0])

  • 3
    $ a -> [0] чище (и избегает ненужного среза одного элемента)
  • 0
    Я бы подумал, что $ a -> [0]
Показать ещё 11 комментариев
15

Наиболее распространенным способом является запуск файлов с чем-то другим, чем

use strict;
use diagnostics;

pjf добавляет: Пожалуйста, будьте осторожны, что диагностика оказывает значительное влияние на производительность. Он замедляет запуск программы, так как он должен загружать perldiag.pod, и до тех пор, пока несколько недель назад он не снизится, он также замедлит и раздувает регулярные выражения, поскольку использует $&. Рекомендуется использовать предупреждения и запуск splain по результатам.

  • 10
    s / диагностика / предупреждения /
  • 0
    Мне больше нравится диагностика
Показать ещё 1 комментарий
14

Использование неинициализированного значения в конкатенации...

Это сводит меня с ума. У вас есть печать, которая включает в себя ряд переменных, например:

print "$label: $field1, $field2, $field3\n";

И одна из переменных - undef. Вы считаете это ошибкой в ​​своей программе - почему вы использовали "строгую" прагму. Возможно, ваша схема базы данных разрешила NULL в поле, которое вы не ожидали, или вы забыли инициализировать переменную и т.д. Но все сообщение об ошибке сообщает вам, что во время операции конкатенации (.) было обнаружено неинициализированное значение. Если бы он сказал вам имя переменной, которая была неинициализирована!

Поскольку Perl не хочет печатать имя переменной в сообщении об ошибке по какой-либо причине, вы в конечном итоге отслеживаете ее, установив точку останова (чтобы посмотреть, какая переменная undef), или добавление кода для проверки состояния. Очень раздражает, когда это происходит только один раз из тысяч в CGI script, и вы не можете легко воссоздать его.

  • 4
    Я не знаю, является ли это Perl 5.10, но имя переменной печатается для меня («Использование неинициализированного значения $ blah в конкатенации ...», «... в сопоставлении с образцом ...» и т. Д.)
  • 0
    Конечно, в этом конкретном примере вы можете посмотреть, что напечатано, и пунктуация скажет вам. Например, «SomeLabel: 1, 3» будет $ field2, что undef. (Я предполагаю, что нет пустых строк, хотя ... "" и undef напечатает то же самое.)
Показать ещё 1 комментарий
14

"мои" объявления должны использовать круглые скобки вокруг списков переменных

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, говорящей, что круглые скобки отсутствуют в "моем" списке. Это показывает, как многие уже знают, почему и правильные, и прагматические предупреждения всегда являются хорошей идеей.

  • 0
    Правильно ли говорить, что my дистрибутив? Я слышал это и есть.
13

Большинство операторов цикла Perl (foreach, map, grep) автоматически локализуют $_, но while(<FH>) нет. Это может привести к странным действиям на расстоянии.

  • 2
    Слова Мудрости: убедитесь, что вы "local $ _;" перед вами "while (<FH>)" во всем, что находится за пределами основного скрипта (например, в подпрограммах).
  • 2
    Использование явной переменной действительно проверяет определенность, попробуйте: perl -MO = Deparse -we' while (my $ line = <>) {} '
Показать ещё 2 комментария
11

Я сделал это один раз:

my $object = new Some::Random::Class->new;

Пришло время найти ошибку. Непрямым синтаксисом метода является eeevil.

  • 2
    Вызов -> new не является панацеей, например * sub Foo {warn "In main :: Foo \ n"} {package Foo; sub new {warn "In Foo-> new \ n"}} my $ x = Foo-> new * Решением для обоих является определение имен пакетов с помощью :: , то есть Foo :: -> new и new Foo ::
  • 0
    MkV, возможно, тебе стоит добавить это в список! Я получил "Не могу найти метод объекта" new "через пакет" 1 "(возможно, вы забыли загрузить" 1 "?)" Определенно более запутанное сообщение, чем я ожидал.
11
my $x = <>;
do { 
    next if $x !~ /TODO\s*[:-]/;
    ...
} while ( $x );

do не является циклом. Вы не можете next. Это инструкция для выполнения блока. Это то же самое, что

$inc++ while <>;

Несмотря на то, что он похож на конструкцию в семействе языков C.

  • 2
    Да, и обходной путь до {{}} довольно уродливый.
  • 3
    используйте пустой блок с redo {my $x = <>; ... ; redo if $condition}
Показать ещё 3 комментария
8

Константы могут быть переопределены. Простым способом случайного переопределения константы является определение константы как ссылки.

 use constant FOO => { bar => 1 };
 ...
 my $hash = FOO;
 ...
 $hash->{bar} = 2;

Теперь FOO - {bar = > 2};

Если вы используете mod_perl (по крайней мере, в 1.3), новое значение FOO будет сохраняться до обновления модуля.

  • 0
    Ой! Я не использовал «использую константу» сам, но если мне понадобится что-то подобное в будущем, я сейчас буду искать что-то более «постоянное» в природе.
  • 10
    Если я правильно помню, в C тоже постоянный указатель означает только то, что вы не можете указать его где-либо еще - фактическое содержимое, на которое он указывает, может быть изменено. С этой точки зрения такое поведение вполне разумно.
Показать ещё 1 комментарий
7

Унарный минус с "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,
);
  • 0
    Да, я не знал, что унарный минус применительно к струне делал что-то волшебное. Интересно, что еще прячется в perlop ...
7

Какие значения вы могли бы ожидать @_ для следующего сценария?

sub foo { } 

# empty subroutine called in parameters

bar( foo(), "The second parameter." ) ;

Я ожидаю получить в баре:

undef, "The second parameter." 

Но @_ содержит только второй параметр, по крайней мере, при тестировании с помощью perl 5.88.

  • 1
    foo ничего не возвращает, даже undef, потому что не имеет выражений для оценки. Я предполагаю, что это спорно, является ли это ошибка или «дизайн» ...
  • 6
    foo () выполняется в контексте списка и возвращает пустой список, который свернут. Чтобы увидеть это, обратите внимание, что «sub foo {return ();}» ведет себя так же.
Показать ещё 1 комментарий
7

Это исправлено в Perl 5.10 - если вам посчастливилось работать где-то, у которого нет аллергии на обновление вещей > : - (

Я говорю о Переменной, которая действительно имеет нулевое значение. Вы знаете, тот, который вызывает неожиданные результаты в таких предложениях, как:

unless ($x) { ... }
$x ||= do { ... };

Perl 5.10 имеет оператор // = или определенный или.

Это особенно коварно, когда действительный ноль вызван некоторым краевым условием, которое не рассматривалось при тестировании до того, как ваш код перешел к производству...

  • 0
    Исправление до 5.10 для этого - ужас "0, но правда". На сайте есть вопрос об этом, если кто-то хочет узнать больше об этом.
  • 0
    «0, но истина» не работает во всех случаях (т.е. пустая строка). Даже когда он работает, он работает, только если вы контролируете источник данных.
Показать ещё 1 комментарий
6

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

  • 1
    Хорошая ошибка, особенно когда вы пишете свои собственные функции. Но ваш совет для борьбы с ним, вероятно, не самый лучший. Вместо того, чтобы читать чужой источник, не полагайтесь на недокументированное поведение. Если вам нужен скаляр, сначала получите список, а затем получите ваш скаляр (длина, сначала и т. Д.).
  • 1
    Заставить ваш код полагаться на эмпирические правила с недокументированными функциями, связывающими ваш код с их реализацией. Если их код изменит синтаксис, ваш код сломается. Просто придерживайтесь документированного поведения, чтобы защитить свой код от этой ситуации. Это известно как уменьшение сцепления.
Показать ещё 1 комментарий
5

Как насчет того, что

@array = split( / /, $string );

не дает тот же результат, что и

@array = split( ' ', $string );

если $string имеет ведущие пробелы?

Это может занять некоторых людей врасплох.

5

Сравнение строк с использованием == и != вместо eq и ne. Например:

$x = "abc";
if ($x == "abc") {
    # do something
}

Вместо:

$x = "abc";
if ($x eq "abc") {
    # do something
}
  • 4
    Смотрите также мою любимую историю об ошибках Perl под названием "Nancy Typing" . Короткая история - "Nancy" != "Nancy" даже если "Paul" == "John"
5

Использование модификатора /o с шаблоном регулярного выражения, хранящимся в переменной.

m/$pattern/o

Задание /o является обещанием, что $pattern не изменится. Perl достаточно умен, чтобы узнать, изменилось ли оно или нет, и повторно перекомпилировать регулярное выражение, поэтому нет веских оснований для использования /o. В качестве альтернативы вы можете использовать qr// (например, если вы одержимы тем, что избегаете проверки).

  • 1
    Это достаточно умно ... не уверен, какой версии. смотрите: perlmonks.org/?node_id=256053 Не используйте / o, используйте qr //
  • 0
    Спасибо, обновлено. Кто-то должен представить документ патч для perlop. (Особенно, если они знают версию Perl, в которой было сделано изменение.)
Показать ещё 1 комментарий
5

Вы не можете локализовать экспортированные переменные, если вы не экспортируете весь типglob.

4

Добавление дополнительных круглых скобок никогда не может изменить значение кода, правильно? Правильно?

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

Интуитивный.

  • 5
    Это хорошо задокументировано. Я знаю, что контексты могут сбивать с толку, если вы новичок в Perl, но они являются фундаментальной частью изучения языка. Но если ты не хочешь учиться, тебя никто не заставит. (Я надеюсь ;)
  • 0
    Я доволен контекстами. Почему x не решает, какой контекст предоставить его левому аргументу, на основании предоставленного ему контекста (в стиле reverse ())? Документировано да, но это не останавливает его от беспричинной несостоятельности.
Показать ещё 7 комментариев
3

Забыв перед отправкой пути к результатам 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, но я думаю, что это все еще довольно распространенная ошибка.

  • 0
    Для этого случая я использую модуль File::chdir . Это позволяет вам изменять рабочий каталог локально для блока. Если вы выполняете свою итерацию в каком-то блоке с local $CWD = $path то вы можете делать что угодно с результатами readdir , тогда, когда вы покидаете блок, все хорошо.
3

Если вы достаточно глупы, чтобы сделать это, 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'}
...
  • 2
    очень хорошо работает в коротких сценариях с обработкой данных и плохо везде
1

Хэш-конструктор - это не что иное, как список, а жирная запятая => - не что иное, как синтаксический сахар. Вы можете укусить это, запутав синтаксис [] 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 ] }
  • 2
    да? Если вы хотели 'foo' => [42, 95, 22] тогда вы должны были написать это ...
  • 1
    Вот почему я сказал «путать». Почему бы новичку не перепутать фигурные скобки () , [] и {} для списков / arrayrefs / hashrefs?
-2

Обработка скаляра как целого:

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

  • 4
    Извините, это не Perl, это был вы. Попробуйте простой тест, такой как perl -E 'say "false" unless (0.0)' чтобы увидеть, что 0.0 действительно ложно-у. Тогда в вашем коде измените $n/=10; $n=int($n/10); чтобы понять, зачем добавлять print $n . "\n" где-то в цикле. Извините, не все ошибки могут быть связаны с языком.
-2

Изменение массива, в котором вы зацикливаетесь в for (каждый), как в:

my @array = qw/a b c d e f g h/;

for ( @array ) {
    my $val = shift @array;
    print $val, "\n";
}

он запутывается и не делает того, что вы ожидаете

  • 3
    Я думаю, что многие языки, кроме Perl, были бы недовольны этим.
-2

Имена пропущенных имен... Я когда-то провел целый день кода устранения неполадок, который не вел себя правильно, только чтобы найти опечатку на имя переменной, что не является ошибкой в ​​Perl, а скорее объявление новой переменной.

  • 3
    Это распространенная ошибка во многих языках. К счастью, использование strict прагмы очень помогает в обеспечении того, что вы ссылаетесь на существующие переменные (которые объявлены с my или our ), а не на создание новых.
  • 11
    Согласен, ошибка не в том, что ваш скрипт запускается с использованием «строгого использования»
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню