- Perl ›
- 豆知識
豆知識 - 知っておくと役に立つ
知っておくと何かとPerlが使いやすくなったり、読みやすくなるかもしれないことをいろいろと追加していきます。
BEGIN
BEGINはコンパイルのときに実行するということをあらわします。
BEGIN { # コンパイルのときに実行したい文 }
require
requireはuseと似ていますが実行時にモジュールを読み込むことと、importメソッドを自動的に実行しないところがことなります。
use File::Basename 'basename'; # 上と同じ意味 BEGIN { require File::Basename; File::Basename->import('basename'); }
動的にモジュールを読み込みたい場合はrequireを使用することがありますが、一般的な用途ではuseで統一するのがわかりやすいでしょう。
local
localはローカル変数を生成するためのものではありません。localは値を一時的に変更してスコープを抜けた段階で値を復元するためのものです。なぜかmyで宣言されたスカラ変数自体には利用することはできません。使用できるのはパッケージ変数とハッシュの要素、配列の要素などです。
our $num = 1; { # 一時的に変更 local $num = 2; } # 1に戻る print $foo; my $scores = {math => 90, english => 100}; { # 一時的に変更 local $scores->{math} = 80; } # 90に戻る print $scores->{math};
けれどもlocalはよほど特殊な用途でない限りは使う場面はありません。ほぼすべての場面でmyですんでしまうので、まずmyで済ませられないかどうかを真剣に検討しましょう。
localについての詳しい解説は以下を参考にしてください。
複数行コメント
複数行コメントの構文はないので、少し残念ですがコメントは#のみになります。デバッグ中に複数行コメントをしたい場合は、ドキュメントとして解釈させることで、コメントアウトすることができます。
=pod my $str = 'aaa'; my $foo = 'foo'; =cut
「=pod」「=cut」として囲まれた部分はドキュメントと解釈され無視されます。
シジルが異なれば別の変数
シジルが異なればまったく別の変数です。
# 配列 my @ids; # スカラ my $ids;
上記の変数はidsという名前を持ちますが、まったく別の変数になります。
特殊変数の意味を調べる
特殊変数はgoogleの検索ではとても探しにくくて苦労します。またドキュメントから探すのも面倒です。perldocコマンドの「-v」オプションを使用すると、英語ですが、特殊変数の意味を知ることができます。
perldoc -v $.
メソッド呼び出し
Perlでは->を使ってメソッド呼び出しを行うことができます。
# メソッド呼び出し SomeClass->method('a', 'b'); $obj->method('a', 'b');
メソッド呼び出しが関数呼び出しと異なるのはサブルーチンの第一引数に->の左側の値が渡されるということです。上記の例ではmethodの第一引数はSomeClassという文字列あるいは$objになります。'a', 'b'は第二引数、第三引数になります。
受け取る側は次のように記述します。
# クラスメソッドの場合 sub method { my ($class, $arg1, $arg2) = @_; } # オブジェクトメソッドの場合 sub method { my ($self, $arg1, $arg2) = @_; }
ミニperldocガイド
perldocコマンドでPerlのドキュメントを見ることができます。
perldoc perlfunc
モジュールのドキュメントも見ることができます。
perldoc File::Basename
モジュールの中身を見たい場合はmオプションを指定します。
perldoc -m File::Basename
モジュールのパスを知りたいときはlオプションを指定します。
perldoc -l File::Basename
標準関数のドキュメントを見たい場合はfオプションを指定します。
perldoc -f substr
リダイレクトしてファイルに落とすと見るのが楽です。
perldoc File::Basename > File-Basename.txt
無名サブルーチン
Perlではsub { } を使うことで無名サブルーチンを作成することができます。
my $twice = sub { my $num = shift; return $num * 2; }
実行するには次のようにします。
my $result = $twice->(5);
デリファレンスの{}の省略
デリファレンスの記号{}は省略することも可能です。
my @array = @$array_ref; # 以下と同じ意味 my @array = @{$array_ref};
単純な変数でない場合は{}が必要です。
my @array = @{$var->nums};
ファイル演算子のドキュメントの見方
-sや-fなどのファイル演算子はよく使用しますが、どうやってドキュメントを見たらよいかがわかりにくいです。コマンドラインで次のようにします。
perldoc -f -X
文字の置換 tr
trというのを時々みるかもしれませんが、tr演算子は文字列の置換を行います。以下の文は「a ⇒ 1」「b ⇒ 2」「c ⇒ 3」という置換を行います。
$str =~ tr/abc/123/;
Perl自体の設定を見る
コマンドライン引数で
perl -V
とします。
ハッシュのリファレンスのハッシュスライス
ハッシュのリファレンスをデリファレンスしてハッシュスライスする記法はとても読みにくいです。
my ($key1, $key2, $key3) = @{$hash}{qw/key1 key2 key3/};
できればひとつづつ代入するほうがよいと思います。
my $key1 = $hash->{key1}; my $key2 = $hash->{key2}; my $key3 = $hash->{key3};
evalのある使い方
eval { 実行文; 1; } or die "Exception";
という記述は良く見かけますが、少し読みにくいです。これの意味はまずevalは{}の中が正常であった場合は最後の実行文の結果を返却します。つまり、1を返却します。この場合はorの右辺は実行されません。失敗した場合はundefを返します。この場合は右辺は実行されます。1;とわざわざ書くのは実行文の戻り値が0で合った場合は成功していても右辺が実行されてしまうからです。
eval { 実行文 }; die "Exception" if $@;
のほうが好みです。
よく使用する特殊変数
- @_ - サブルーチンの引数
- @ARGV - コマンドライン引数
- $. - ファイルを読み込んだ場合の行番号
- $0 - スクリプト名
- $1 - 正規表現の括弧()にマッチした部分。$2, $3 も同じ意味
- $! - OSのエラーメッセージ
- $@ - evalで例外をキャッチした場合の例外メッセージ
- $? - 子プロセスのステータス
shift関数のデフォルト引数
shift関数で引数を指定しなかった場合、トップレベルであれば引数はコマンドライン引数(@ARGV)、サブルーチンの中であればサブルーチンの引数(@_)になります。単独のshiftはよく使われます。
# トップレベル my $num = shift; # 以下と同じ意味 my $num = shift @ARGV;
# サブルーチンの中 sub { my $num = shift; # 以下と同じ意味 my $num = shift @_; }
外部プロセスの終了ステータス
system関数などで外部プロセスを呼び出したとき、外部プロセスの終了ステータスは$?という特殊変数の上位8bitの中に入っています。コマンドの呼び出し自体が失敗した場合は$?に-1が設定されます。ですから、終了ステータスが正常(0であれば正常)であることを調べるには次のようにします。
my $command = "ls -l"; system $command; if ($? == -1) { die "failed to execute: $!\n"; } elsif ($? >> 8 != 0) { die "Return error status\n"; }
Perlのithreadの実装はあまりよくない
Perlではithreadと呼ばれる軽量スレッドの実装がありますが、あまり実装がよろしくないといううわさです。またCPANモジュールはスレッドセーフであることを意識して作成されていないので、思わぬバグに突き当たります。またネイティブスレッドはサポートしていません。自動的にスレッドを切り替える処理はPerlで書くのはあきらめたほうが無難でしょう。
自分でスレッドを切り替える用途であればコルーチンであるCoroが使えるでしょう。
古典Perl
書くときは使わないですが、読むときに古典的な文法に遭遇するかもしれません。
varsプラグマは、ourで宣言したのと同じ効果をもちます。
# our $VAR; と同じ use vars 'VAR';
覚えておきたいqで始まる演算子
qで始まる演算子は覚えにくいのでまとめておきます。また囲む文字には//や{}や##や||などを使うことができます。qはシングルクォート演算子です。シングルクォートが使える以外はシングルクォートと同じです。
my $str = q/aaa ''' bbb/;
qqはダブルクォート演算子です。ダブルクォートが使える以外はダブルクォートと同じです。
my $str = qq/aaa """ $str/;
qwは文字列リスト演算子です。文字列のリストを簡単に書くことができます。
# ('foo', 'bar', 'baz') と同じ my $str = qw/foo bar baz/
qrは正規表現のリファレンスです。
my $regex_ref = qr/^aaa.*bbb$/ms;
モジュールの末尾には1;が必要
モジュールの末尾には1;が必要です。1でなくても真の値であれば何でもかまいませんが、普通は1;を使います。
package SomeModule; # 実装 1;
ブロックの最後にはセミコロンは不要。評価が最後の場合はreturnは不要。
ブロックの最後であればセミコロンはあってもなくてもかまいません。
sub func1 { my $arg = shift; # このセミコロンはあってもなくてもよい。 return $arg; }
また戻り値はそれが最後の評価になる場合はreturnが必要ではありません。
sub func1 { my $arg = shift; # returnは合ってもなくてもよい。 $arg; }
通常はreturnもセミコロンもつけておいたほうがよいでしょう。関数を一行で書く場合はセミコロンもreturnも使わないほうがきれいに書けることが多いようです。
# 定数など sub CONST_VALUE { 3 }
たくさんPerlを試したいとき
わたしはどこかで見つけたサンプルを実行したい場合はa.plという簡単なファイルを用意して、そこで実験するようにしています。削除するのも面倒なので、新しく実験したい場合は「__END__」を使えば、スクリプトはそこで終了とみなされます。
perl a.pl
use strict; use warnings; print 'aaa'; __END__ print 'bbb';
デバッガで最終行を実行したときにプログラムを終わらせない
Perlのデバッガを使ったときに最終行を実行するとプログラムが終わってしまうのを避けたい場合は次のように意味のない「1;」という実行文を最終行に入れます。
my $str = 'aaa'; print $str; 1;
ファイル読込のショートカット
使い捨てのスクリプトを作成する場合は、ファイルオープンの記述を行うのは少し面倒です。ダイヤモンド演算子を単独で使えば、コマンドライン引数、あるいは標準入力から1行づつファイルの読込ができます。
# コマンドライン perl script.pl file1 file2
# ファイルから1行づつ読み込み while (my $line = <>) { ... }
この記述は
while (defined(my $line = <>)) { ... }
と同じ意味を持つので0だけが含まれる行なども、正しく読み込んでくれるので安心です。
変数展開の注意
変数名の後にコロンやアンダーバーが続いていると正しく変数展開を行うことができません。そのような場合は「{}」でくくって明示的に変数名を示します。
my $name = 'aaa'; my $message = "${name}::aaa ${name}_ppp";
シンボルテーブルを覗いてみる
変数やサブルーチンなどの名前が登録されているデータ構造です。各パッケージのシンボルは「::」をパッケージ名の末尾に付けた変数に格納されています。
# シンボルテーブル %main:: %CGI:: %File::Basename::
each関数はwhileの中だけで使う
each関数は内部的にイテレータを持っているので、一度だけ使うとイテレータが進んだままになってしまいます。
# イテレータが進んだままになってしまう。 my ($key, $value) = each %hash;
keys関数を作用させるとイテレータはリセットされますが、あまり良い方法ではないでしょう。
# イテレータのリセット keys %hash;
each関数はwhile文のループの中でだけ利用しましょう。
while(my ($key, $value) = each %hash) { ... }
print関数に渡すファイルハンドルは第一引数ではない
変な仕様です。print関数に渡すファイルハンドルは第一引数ではないです。ファイルハンドルの後ろにはカンマがありません。
# $fhは第一引数ではない。 print $fh $output;
これは間接オブジェクト記法と呼ばれ次と同じ意味を持ちます。
$fh->print($output);
WindowsではIO::Pollやselectはソケットに対してしかうまくいかない
Windowsでは非同期IO処理はソケットに対してしか行うことができません。ファイルに対してはできません。
Windowsではglob関数の引数に空白が含まれているとうまく動かない
Windowsではglob関数の引数に空白が含まれているとうまく動きません。なんとかこれをうまくいかせるには、ダブルクォートで囲む必要があります。
my @files = glob('"C:/Documents and Settings/*.*"');
サブルーチンの括弧が必要な場合
サブルーチンがすでに宣言されていれば括弧はいりません。
# すでにサブルーチンが宣言されている場合は括弧がいらない。 sub func1 { print $_[0] } func1 'aaaa';
インポートした場合も同じです。
# インポートした場合も括弧はいらない use Carp 'croak'; croak 'aaa';
後ろで宣言されている場合は括弧が必要です。
# 後ろで宣言されている場合は括弧が必要 func1('aaa'); sub func1 { print 1 }
無名サブルーチンは最初から最後まで存在する
動的に作成したように見える無名サブルーチンはプログラムの最初にコンパイルされて、プログラムの最後に破棄されます。次のコードで生成されるのはサブルーチンのリファレンスであって、サブルーチンそのものではないです。ごちゃまぜになりやすいので注意。
{ # 生成されるのはサブルーチンのリファレンスでサブルーチンそのものではない my $sub = sub { ... } }
正規表現でのリストコンテキスト
正規表現を使ってマッチした文字の一部分を取得したい場合はif文と組み合わせて次のように書きます。
if ($str =~ /正規表現/) { my $match1 = $1; my $match2 = $2; }
もうひとつ書き方があって、正規表現をリストコンテキストで評価してマッチした文字を取得することもできます。
my ($match1, $match2) = $str =~ /正規表現/;
正規表現の囲み文字の変更
通常は正規表現は次のように書きます。
$str =~ /正規表現/
もうひとつ同じ意味を持つ書き方があります。mを使って上記と同じ意味になります。
$str =~ m/正規表現/;
これは何のためにあるかというと正規表現の中で「/」がたくさん出てくる場合にエスケープするのはとても見にくくなると思った場合に正規表現の囲み文字を変えることができます。囲み文字を「#」に変更した例です。
$str =~ m#http://aaa.com#;
ord関数はordinalの略
文字列を渡すとコードポイントを返却してくれるord関数はordinalの略。これは日本語に訳すと序数という意味。ちなみに対になっているchr関数はコードポイントを渡すと文字に変換してくれます。
ord関数 <-> chr関数
orは「左辺が真でなければ」と英文的に読みます。
orはもともと論理演算子なのですが、論理演算子としては使われずに条件分岐に使われることがほとんどです。
A or B
Perlのor演算子は左辺(A)が真であった場合は、右辺(B)の結果がどうであろうと「A or B」は真になるので、右辺を実行しないという特徴があります。条件分岐に利用される場合はこの特徴が利用されています。
つまり左辺(A)が真の場合には右辺(B)は実行されす、左辺(A)が偽の場合は右辺(B)が実行されます。英文的に読むと「左辺(A)が真でなければ右辺(B)を実効する」と読めます。
設定ファイルをPerlで記述する
設定ファイルはPerlで記述するのが便利です。設定ファイルを読み込むにはdoを使用します。
my $conf_file = "app.conf"; my $conf = do $conf_file or die qq/Can't load config file "$conf_file": $!$@/;
設定ファイルの内容です。
{ name => 'Foo', number => 9 }
ファイルの読み込みか解析に成功しなかった場合は未定義値が返却されます。ファイル名が存在しなかった場合は$!に、Perlのソースコードの読み込みに失敗した場合は$@にエラーの内容が設定されます。
なぜか今までわたしはXMLとかJSONで設定ファイルを書いていましたが、よく考えるとPerlで書くのが一番楽ですね。
注意点ですが、自分の手で書いた設定ファイル以外をdoで読み込んではいけません。さもなければ任意のスクリプトが実行されてしまう危険があるからです。