web-dev-qa-db-ja.com

Perlの一般的な落とし穴?

Perlの隠された機能 に関する質問は少なくとも 1つの応答 をもたらしました。これは機能または誤機能のいずれかと見なすことができます。この質問をフォローアップすることは論理的であるように思われました:Perlの一般的な非自明な間違いは何ですか?動作するはずのように見えるが、動作しないもの。

回答を構成する方法や、「簡単すぎて」落とし穴と見なすことができないものについてのガイドラインは提供しません。それが投票の目的だからです。

回答表

構文

セマンティクス/言語機能

デバッグ

ベストプラクティス

メタアンサー

関連項目: ASP.NET-一般的な落とし穴

75
Adam Bellaire

識別子の::を一重引用符で置き換えることができるという事実。

考えてみましょう:

use strict;
print "$foo";        #-- Won't compile under use strict
print "$foo's fun!"; #-- Compiles just fine, refers to $foo::s

次の問題につながります。

use strict;
my $name = "John";
print "$name's name is '$name'";
# prints:
#  name is 'John'

これを回避するための推奨される方法は、変数名を中括弧で囲むことです。

print "${name}'s name is '$name'";
# John's name is 'John'

そしてまたuse warnings、未定義の変数の使用について説明するので$name::s

38
Adam Bellaire

字句ファイルハンドルに出力できます:good。

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

これは完全に直感的でない動作です。これについて聞いていない場合、またはドキュメントで読んでいない場合は、自分で理解できるとは思えません。

28
dland

これはメタアンサーです。多くの厄介な落とし穴が Perl :: Critical によってキャッチされます。これは、コマンドラインから perlcritic コマンドを使用してインストールし、実行できます。 Perl :: Critical Webサイト を介して、インターネット経由でコードを送信できますが、オプションをカスタマイズすることはできません。

Perl::Criticは、ページ番号を含む、Damian ConwaysPerl Best Practicesの本への参照も提供します。したがって、本全体を読むのが面倒な場合は、Perl::Criticは、あなたが読んでいるはずのビットを教えてくれます。

28
pjf

PerlのDWIMmerは、字句ファイルハンドルで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
18
Michael Carman

最も一般的な落とし穴は、ファイルを別のもので開始することです

use strict;
use diagnostics;

pjf 追加:診断はパフォーマンスに重大な影響を与えることに注意してください。 perldiag.podをロードする必要があるため、プログラムの起動が遅くなり、数週間前のbleadperlまでは、$&を使用するため、正規表現も遅くなり、肥大化します。警告を使用し、結果に対してsplainを実行することをお勧めします。

16
Vinko Vrsalovic

perltrap のマンページには、タイプ別に整理された不注意なトラップの多くがリストされています。

16
Michael Carman

紛らわしい参照と実際のオブジェクト:

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

$a->[0](最高)、$$a[0]@{$a}[0]、または@$a[0]のいずれかである必要があります)

15
Vinko Vrsalovic

配列をスカラーに割り当てることは私には意味がありません。例えば:

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

'c'を$ fooに割り当て、配列の残りの部分を破棄します。これはもっと奇妙です:

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

これは最初の例と同じことをするはずですが、代わりに$foo@fooの-​​lengthに設定するので、$foo == 3になります。

15
Graeme Perrow

「my」宣言では、変数のリストを括弧で囲む必要があります

use strict;
my $a = 1;
mysub();
print "a is $a\n";

sub {
    my $b, $a;   # Gotcha!
    $a = 2;
}

印刷します aは2ですmy宣言は$bにのみ適用されるためです(その行での$aの言及は単に何もしませんでした)。 「usestrict」が有効な場合でも、これは警告なしに発生することに注意してください。

「警告の使用」(または-wフラグ)を追加すると、Perlが「my」リストの周りに括弧がないと言って状況が大幅に改善されます。これは、多くの人がすでに知っているように、厳密なプラグマと警告プラグマの両方が常に良い考えである理由を示しています。

15
andy

連結での初期化されていない値の使用...

これは私を夢中にさせます。次のようないくつかの変数を含むプリントがあります。

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

そして変数の1つは undef。これはプログラムのバグだと考えています。そのため、「厳密な」プラグマを使用していました。おそらく、データベーススキーマが予期しないフィールドでNULLを許可したか、変数を初期化するのを忘れたなどです。しかし、すべてのエラーメッセージは、連結中に初期化されていない値が検出されたことを示しています(.)操作。初期化されていない変数のnameを教えてくれれば!

Perlは何らかの理由でエラーメッセージに変数名を出力したくないので、ブレークポイントを設定して変数名を追跡することになります(どの変数が undef)、または条件をチェックするコードを追加します。 CGIスクリプトで数千回に1回しか発生せず、簡単に再作成できない場合は、非常に煩わしいです。

15
andy

Perlのほとんどのループ演算子(foreachmapgrep)は自動的に_$__をローカライズしますが、while(<FH>)はローカライズしません。これは、奇妙な遠隔作用につながる可能性があります。

14
Michael Carman

私はこれを一度しました:

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

私を連れて行った年齢エラーを見つけるために。間接メソッドの構文はeeevilです。

12
Dan
my $x = <>;
do { 
    next if $x !~ /TODO\s*[:-]/;
    ...
} while ( $x );

doはループではありません。 nextはできません。ブロックを実行するための命令です。それはと同じことです

$inc++ while <>;

それにもかかわらず、それは言語のCファミリーの構造のように見えます。

11
Axeman

定数は再定義できます。定数を誤って再定義する簡単な方法は、定数を参照として定義することです。

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

これでFOOは{bar => 2}です。

Mod_Perlを使用している場合(少なくとも1.3では)、新しいFOO値はモジュールが更新されるまで存続します。

9
cwhite

この落とし穴はPerl5.10で修正されています-運が良ければ、アップグレードにアレルギーのない場所で作業することができます> :-(

私は話します 有効にゼロである変数。 あなたが知っている、予期しない結果を引き起こすものは次のような節になります:

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

Perl5.10には// =または 定義済み-または オペレーター。

これは、コードが本番環境に移行する前のテストで考慮されなかったエッジ条件が有効なゼロの原因である場合に特に陰湿です...

8
RET

"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,
);
8
Chas. Owens

次のシナリオでは、@_にどのような値が含まれると思いますか?

sub foo { } 

# empty subroutine called in parameters

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

barで受け取ることを期待します:

undef, "The second parameter." 

ただし、@_には、少なくともPerl 5.88でテストする場合は、2番目のパラメーターのみが含まれます。

7
Tobi Ronfort

eqneの代わりに==!=を使用して文字列を比較します。例えば:

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

の代わりに:

$x = "abc";
if ($x eq "abc") {
    # do something
}
6
Nathan Fellman

変数に格納されている正規表現パターンで/o修飾子を使用します。

m/$pattern/o

/oを指定することは、$patternが変更されないという約束です。 Perlは、変更されたかどうかを認識して正規表現を条件付きで再コンパイルするのに十分賢いので、/oを使用する正当な理由はもうありません。または、qr//を使用することもできます(たとえば、チェックを回避することに夢中になっている場合)。

6
Michael Carman

Graeme Perrowの答え 良かったですが、さらに良くなりました!

リストコンテキストでNiceリストを返す典型的な関数を考えると、次のように尋ねるでしょう。スカラーコンテキストで何を返すのでしょうか。 (「典型的」とは、ドキュメントに記載されていない一般的なケースを意味し、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、呼び出し元の文字は、呼び出された関数のソースコードを毎回うろつくほうがいいです

6
j_random_hacker

その事実はどうですか

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

と同じ結果は得られません

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

$ stringに先頭にスペースがある場合は?

それは一部の人々を驚かせるかもしれません。

6
AmbroseChapel

Typeglob全体をエクスポートしない限り、エクスポートされた変数をローカライズすることはできません。

5
Michael Carman

括弧を追加することはできませんコードの意味を変更する、そうですか?正しい?

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

直感的。

4
j_random_hacker

あなたがそうするのに十分愚かであるならば、Perlはあなたが同じ名前で複数の変数を宣言することを可能にします:

my ($x, @x, %x);

Perlは変数typeではなくcontextを識別するためにシジルを使用するため、特に$xが参照である場合、後のコードが変数を使用するときに混乱がほぼ保証されます。

$x[0]
$x{key}
$x->[0]
$x->{key}
@x[0,1]
@x{'foo', 'bar'}
@$x[0,1]
@$x{'foo', 'bar'}
...
4
Michael Carman

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's odd...
}

# Scratching head...let's see...
use Data::Dumper;
print Dumper @files;
# Whoops, there it is...

この落とし穴はreaddirのドキュメントに記載されていますが、それでもかなり一般的な間違いだと思います。

3
Telemachus

ハッシュ「コンストラクター」はリストにすぎず、=>ファットコンマは構文糖衣にすぎません。 []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 ] }
1
hhaamu