Perl Hackers Hub

第32回新人さんのためのPerl入門(3)

(1)はこちら、(2)はこちらから。

そのほかに注意するポイント

(3)では筆者の体験談や、はまりがちなポイントについて紹介します。少しでもミスやバグが減ると幸いです。

undefの扱い方に注意する

変数を未定義にしたいときにはシステム関数であるundefを使いますが、配列を未定義にしたいときに@y= undefのように単にundefを代入すると、違う結果になるので注意です。

use Data::Dumper;
my @a = undef;
print Dumper \@a; # $VAR1 = [
                  #           undef
                  #         ];
my @b = ();
print Dumper \@b; # $VAR1 = [];

変数@aundefの戻り値を代入しようとしたときだけ、要素数が1で$a[0]undefが入った配列ができてしまいます。

同様に、関数を作ったときにundefを配列に返すのも注意が必要です。

sub something1 {
    return undef;
}
my @array1 = something1();
print Dumper \@array1; # $VAR1 = [
                       #           undef
                       #         ];

しかし、次のようにハッシュの値に関数からの戻り値を入れたい場合、return;のままだと警告が出て、キーと値がずれてしまいます。Perlのハッシュは実際にはキーと値が順番に並んでいる配列なので、要素が何もないとそのぶん関係性がずれてしまうからです。

my %hash = (
    keyA => 12345,
    keyB => something2(),
    keyC => undef,
);
print Dumper \%hash;

# Odd number of elements in hash assignment at..
# Use of uninitialized value in list assignment at..
# $VAR1 = {
#      'keyA' => 12345,
#      'keyB' => 'keyC',
#      '' => undef
#    };

そこでPerl 5.10から導入されたDefined-or演算子を使います。次のように//のあとに値を書きます。左辺が定義されている場合はそのまま、定義されていない場合は右辺の値を返します。

keyB => something2() // undef,

# $VAR1 = {
#      'keyA' => 12345,
#      'keyB' => undef,
#      'keyC' => undef
#    };

出力のバッファリング

Perlは出力のたびにシステムコールしないようバッファリングを行っており、改行かプログラムの終了時にまとめて出力されます。そのためループの処理中にプログレスバーなどを出したいときには、改行も出力するか、$| = 1をループの前に書きます。特殊変数$|は、出力のバッファリングを抑制します。

$| = 1;
foreach ( 0..10 ){ sleep 1; print"+"; }

ファイルへ出力する場合、メソッドautoflush(1)を呼び出すことでバッファリングが無効になります。

open my $out, '>', 'output.txt' or die $!;
$out->autoflush(1);

||とorの違い

Perlでは演算子||の同義語としてorがありますが、完全に同じ動作をするわけではありません。演算子にはそれぞれ優先度が設定されており、その順番に処理されます。

たとえばin.txtが存在しない状態で、次のopenを実行したときにどのような動作をするのでしょうか。

open my $file1, "<", "in.txt" or die $!;
open my $file2, "<", "in.txt" || die $!;

こういったコードの断片を試すにはワンライナーが一番手軽です。-eオプションを付けてコードをシングルクォーテーションで囲い、引数にしてperlを実行してみてください。

$ perl -e 'open $file1, "<", "in.txt" or die $!;'
No such file or directory at -e line 1.

orを使うとNo such file or directoryとエラーが表示され異常終了します。

$ perl -e 'open $file1, "<", "in.txt" || die $!;'
$

||を使うとdieもされず正常終了しています(シェルの特殊変数$?で確認できます⁠⁠。

重要なのが演算子の優先度です。orの優先度は一番低いため、openが成功しているかどうかの部分が見られます。||を使った場合は,よりも優先度が高いため、"input.txt" || die $!を先に見て、"input.txt"は文字列であり、真なのでdieしないままopenが実行されてしまいます。こういった演算子の情報を得るには、perldoc perlopで参照できます。

notと!の違い

否定を表すnot!についても優先度があり、扱い方によっては意味が変わってきます。

次の2つのif文ではどうでしょうか。

if (not $x && $y)
if (! $x && $y)

not ! &&の3つの演算子の優先度はperldoc perlopによるとnot < && < !となっています。これを当てはめると1番目はif(not($x && $y))となり、$xは真かつ$yも真でなければの意味で、unless($x && $y)と同義となります。2番目はif((!$x) && $y)で、$xは偽かつ$yは真であればとなります。

今回はこれをB::Deparseというモジュールを使って確認してみます。このモジュールはPerl側でのソースコードの解釈を出力してくれます。B::DeparseはPerlのワンライナーでオプションとして-MO=Deparseを付けて実行するのが手軽です。先ほどのif文とunless($x&& $y)の3つをそれぞれ次のように実行してみます。

$ perl -MO=Deparse,-p -e 'print 1 if (not $x && $y)'
(($x and $y) or print(1));

$ perl -MO=Deparse,-p -e 'print 1 if (! $x && $y)'
((not($x) and $y) and print(1));

$ perl -MO=Deparse,-p -e 'print 1 unless ($x && $y)'
(($x and $y) or print(1));

このように処理される順番に括弧がついて出力されます。括弧を外したい場合は-pなしで実行します。

Perlの勉強会

最後にPerlの情報収集や勉強会について紹介します。

YAPCとはYet Another Perl Conferenceの略で、世界規模で行われているPerlのカンファレンスです。このYAPCのアジア版がYAPC::ASIAであり、現在、運営は本連載の監修でもあるJapan Perl Assosiationが行っています。2006年から毎年行われており、今年のYAPC::ASIA 2015は8月に東京ビックサイトで開催予定です。YAPC::ASIAの公式Webサイトで過去の発表動画が公開されているので参考にしてみてください。

ほかにも地方pmPerl Mongersと呼ばれる各地でのPerlの勉強会や、プログラミング未経験者からPerl初級者向けを対象としたPerl入学式が東京、大阪、福岡で行われています。どの勉強会でもCPAN Authorから初心者まで幅広く参加者がいます。

また、毎年12月に行われている各技術系Advent Calenderに、Perlのカテゴリもあります。Tipsやモジュール紹介、Perl製のWebサーバ関係の話題や勉強会のレポートなど、どれも非常に読み応えがあり参考になります。

まとめ

Perlの歴史は長く、グッドプラクティスはもちろん、今やバットプラクティスとされているものもたくさんあります。モジュールについても、今となっては使うべきでなかったり、過去にデファクトスタンダードとされていたものも今ではより良いモジュールが公開されている場合があります。そういった知見や先輩のPerl Mongerたちの発信している情報をキャッチしていくことが大事です。また、日ごろ使っているモジュールのソースコードを眺めるのもすごく参考になり、力がつくのでお勧めです。PerlのモットーであるTMTOWTDI There's More Then One Way To Do Itやり方は一つじゃない)も忘れずに!

さて、次回の執筆者はhayajoさんで、テーマは「Mojoliciousで簡単Webアプリケーション開発」です。お楽しみに。

おすすめ記事

記事・ニュース一覧