Perl 再入門 : 配列・連想配列 (ハッシュ)・関数呼び出し・例外処理
Perl 再入門、第2弾。
目次
配列を扱う
Perl での配列の扱い方。
# 配列の定義 : 「@」で定義する
my @fruits = ("apple", "orange", "banana");
# 添字で指定して取り出す : 単一要素を取得する際は「$」を使う
print $fruits[0];
# 配列の長さを取得する (JavaScript でいう array.length)
print @fruits;
# 配列の最終要素の添字を取得する (JavaScript でいう array.length - 1)
print $#fruits;
# for を使って順に表示する
for(my $i = 0; $i < @fruits; $i++) {
print $fruits[$i];
}
# next; → Java でいう continue 相当
# last; → Java でいう break 相当
# foreach を使って順に表示する
foreach my $fruit (@fruits) {
print $fruit;
}
push(@fruits, "peach"); # 末尾に追加
pop(@fruits); # 末尾から削除
unshift(@fruits, "peach"); # 先頭に追加
shift(@fruits); # 先頭から削除
こんな感じ。個人的についつい JavaScript との比較になってしまうが、よく似ているかなと思う。
特徴的なのは、配列変数の宣言時にアットマーク @
を使う点。変数を見るだけで配列だなと分かるのはありがたい。
use strict;
前提で書いているので、for
文における $i
、foreach
文における $fruit
の宣言時にも my
を使用している。
連想配列 (ハッシュ) を扱う
Perl で連想配列、いわゆるハッシュを作るには、以下のように書く。
# 変数定義はドル「$」ではなくパーセント「%」
my %myHash = (
"name" => "my-hash",
"version" => "0.0.0"
);
# とりあえず全部出力する
print %myHash;
他に、無名ハッシュ {}
というモノを使って、ハッシュへのリファレンス (参照) を作るやり方もある。
# 変数定義はドル「$」、つまりスカラ変数
my $myRecord = {
"name" => "my-record",
"version" => "0.0.0"
};
# とりあえず全部出力する (リファレンスをデリファレンスして取り出す)
print %{$myRecord};
リファレンスっていうのが謎い。
ハッシュから値を取り出す
ハッシュから特定の値を取り出すには以下のように書く。
# ハッシュ
my %myHash = (
"name" => "my-hash",
"version" => "0.0.0"
);
print $myHash{'name'};
# ハッシュのリファレンス
my $myRecord = {
"name" => "my-record",
"version" => "0.0.0"
};
print $myRecord->{'name'};
JavaScript の myHash.name
とか myHash['name']
みたいな書き方と同じ。ハッシュのリファレンスから取り出す時は ->
が必要になるのが違い。
連想配列からキーや値を全部取り出す
Key と Value を全部取り出して順に出力したりするには、each
関数を使ってこんな感じに書く。
# ハッシュ
my %myHash = (
"name" => "my-hash",
"version" => "0.0.0"
);
while(my ($hashKey, $hashValue) = each %myHash) {
print $hashKey . " : " . $hashValue . "\n";
}
# ハッシュのリファレンス
my $myRecord = {
"name" => "my-record",
"version" => "0.0.0"
};
while(my ($recordKey, $recordValue) = each %{$myRecord}) {
print $recordKey . " : " . $recordValue . "\n";
}
キーだけ取るなら keys
・値だけ取るなら values
関数がある。
JavaScript の場合、連想配列の順序はわりかし保たれている印象があるが、Perl だと実行する度にコロコロと順序が変わる。
環境変数を引っこ抜くサンプル
ハッシュの扱い方が分かってきたので、試しに環境変数のハッシュである %ENV
の中身を画面に表形式で出力してみる。
print "<table border=\"1\">";
while(my ($hashKey, $hashValue) = each %ENV) {
print "<tr><th>" . $hashKey . "</th><td>" . $hashValue . "</td></tr>";
}
print "</table>";
コレだと出力順序がコロコロ変わるので、keys
関数でキーを先に取得し、それを sort
してから出力してみよう。
# %ENV からキーを取得し昇順にソートする
my @keys = keys %ENV;
my @keys = sort { $a cmp $b } @keys;
print "<table border=\"1\">";
foreach my $key (@keys) {
print "<tr><th>" . $key . "</th><td>" . $ENV{$key} . "</td></tr>";
}
print "</table>";
コレで良い感じ。REQUEST_METHOD
とか QUERY_STRING
とかが見えるので、CGI での処理も良い感じにできそう。
- 参考 : Perlのハッシュを理解しよう - Perlゼミ(サンプルコードPerl入門)
- 参考 : perldata - Perl のデータ型 - perldoc.jp
- 参考 : ハッシュのリファレンス - Perlゼミ(サンプルコードPerl入門)
- 参考 : 環境変数を取得・設定する %ENV - Perlゼミ(サンプルコードPerl入門)
カレントディレクトリ配下のファイル名を取得するコードを元に、関数の呼び出し方について
Perl の opendir
という関数を使うと、Perl CGI ファイルがあるディレクトリを起点に、ディレクトリとファイルの一覧が取得できる。
# 変数 $dir はファイルハンドル。ココではディレクトリを読み込んだり閉じたりする時に使う
my $dir;
opendir $dir, "./";
my @files = readdir $dir;
closedir $dir;
# 取得したファイル名を順に表示する
foreach my $file (@files) {
print $file;
}
opendir
・readdir
・closedir
は組み込みの関数。関数呼び出しはカッコ ()
を付けても付けなくても呼べる。
my $dir;
opendir($dir, "./");
@files = readdir($dir);
closedir($dir);
カッコの有無は、標準関数やモジュールからインポートした時の場合は特に変わりがない。sub
でサブルーチンを定義した場合に、関数定義よりも手前に関数呼び出しがある場合は、カッコ ()
が必須。
また、関数名の手前にアンパサンド &
を指定しても関数呼び出しができる。
sub myTest {
print "\nmyTest\n";
}
# 関数定義が手前にあるので、以下のどれでも動く
myTest;
myTest();
&myTest;
&myTest();
# 関数定義が後にあるので、コチラは実行されない
myTest;
# ↓ コチラなら実行される
myTest();
&myTest;
&myTest();
sub myTest {
print "\nmyTest\n";
}
どういう使い分けのルールがあるのかよく分からないが、こんな意見もあった。
◆ 関数名の後の『()』小カッコについて
組み込み関数やインポートしたモジュール関数は、『()』小カッコを付けない方がよい。
カッコを付けてもOKですが、ユーザ定義関数以外であることを明示するために『()』を付けない方がよい。ユーザが定義できる関数(サブルーチン)は、『()』小カッコを付ける。
関数をロジックの前置きに定義すると、カッコなしでもOKですが、そうしない。『()』小カッコを付けてユーザ定義関数を明示する。
呼び出し元の書き方の違いで、ユーザ定義関数だと分かるように、ユーザ定義関数にはカッコを付ける、というのが良いのかも。
例外処理
Perl の標準では、try
・catch
という構文はない。代わりに eval
と if
を使った書き方が多い。
my $dir;
eval {
opendir($dir, "./no-dir") or die "Can't open the directory. $!";
};
if($@) {
print("MyError : $@");
exit 1;
}
# 正常時の処理…
my @files = readdir($dir);
closedir($dir);
eval
が try
部分。例外を吐きそうなところには or die()
という関数を使い、throw
するエラーメッセージを用意しておく感じ。$!
とすると標準のエラーメッセージが返せるので、or die($!)
が一番シンプル。eval
はブレースで囲まれているがセミコロンが必要なので、eval {};
となる。
直後の if
ブロックが catch
相当。$@
がエラーメッセージになるので、その有無で判定する。
もし上手くいかない場合は、こんな内容が print
される。
MyError : Can't open the directory. at /Library/WebServer/CGI-Executables/test.cgi line 16.
line 16
というのは opendir
部分のこと。
- 参考 : opendir関数 - ディレクトリをオープンする - Perlゼミ(サンプルコードPerl入門)
opendir
関数について
- 参考 : サブルーチンの作成方法 - Perlゼミ(サンプルコードPerl入門)
- 参考 : Perl基礎入門 | KentWeb
- サブルーチンの定義と呼び出し方について
- 参考 : Perlのエラートラップ(例外処理)私的まとめ - OPS(元)-DEV(現)なEngineerの日記
- 例外処理について。他のモジュールを入れれば
try
・catch
風なこともできる
- 例外処理について。他のモジュールを入れれば
今回はココまで
配列や連想配列の扱い、ファイルやディレクトリの操作、例外のハンドリング方法などが分かってきて、段々と CGI でよくやる処理への備えができてきた感じ。