スライス

リストスライス

リストから複数の要素をまとめて取り出す。

my($day, $mon, $year) = (localtime)[3, 4, 5];

リストの要素、リストを生成する式は必ずカッコで囲まないといけない。

配列スライス

配列の要素をまとめて取り出す。配列の場合はカッコで囲む必要はない。

my @list = $nums[3, 6, 0, 3];


複数の要素をまとめて更新する。

my @list[3, 8] = ($new_id, $new_name);

ハッシュスライス

ハッシュの要素をまとめて取り出す。%ではなく@を使う。

my @list = @url{ qw/ yahoo google hatena / };

ハッシュに値をまとめてセットする。

my %zip;
@zip{ @area } = @code;

my @zip{ @area } = @numbers; # これはエラー

mapを使ってリストを変換する

my @list = (3.45, 29, 1446, 453456, -34, 0.0004);
print map { sprintf("%6g\n", $_) } @list;
  3.45
    29
  1446
453456
   -34
0.0004

map演算子はリストの要素を$_に代入して、ブロック内のコードを実行し、その式の結果をリストの要素として返す。リストの要素をまとめて処理する時に役立ちそう。mapの場合も$_を変えると、元の要素も変わるし、ブロック内が1個の式なら、ブロックを省略できる。

grepを使ってリストから要素を読み出す

my @nums = grep { $_ % 2 } 1..1000;

grep演算子は1番目の引数に$_を使ったコードのブロックを、2番目の引数に処理すべき要素のリストを受け取る。リストの要素を$_に代入して、ブロック内のコードを実行する。そして、真になった要素だけを結果のリストにして返す。上の例だと、1〜1000までの奇数のリストが返される。$_はforeachループの制御変数と同じで、$_の値を変えると、元の要素も変わる。ブロックの中が1個の式なら、ブロックを省略できる。

my @nums = grep $_ % 2, 1..1000;

高度なソート

ソートサブルーチンを使うことで、任意の順番で文字列を並び替えることができる。

sub by_reverse_num {
    if ($a > $b) { -1 }
    elseif ($a < $b) { 1 }
    else { 0 }
}

これは数値として降順に並び替えるたソートサブルーチン。
ソートサブルーチンの引数は自動的に$aと$bに代入される。そして、$aが$bより先にあるべきなら-1を、$bの方が先にあるべきなら1を、順番がつけられない場合は0を返すようにする。また、ソートサブルーチンの中で、$aと$bを宣言したり、値を代入すると、ソートサブルーチンは正しく動作しない。


ソートサブルーチンを使うには、sortとリストの間にソートサブルーチンを置く。

my @reverse_nums = sort by_reverse_num @nums;


ソートサブルーチンがシンプルなものであれば、たいていの場合、インラインで書く。

@reverse_nums = sort { $b <=> $a } @nums;

<==>は左辺の方が小さければ-1を、左辺の方が大きければ1を、両辺が同じであれば0を返す。


文字列を比較する場合は、cmp演算子を使う。cmpの並び順はデフォルトのソートと同じなので、単に$aと$bを比較しても意味はない。

@sorted = sort { "\L$a" cmp "\L$b" } @strs;

これは、大文字と小文字を区別せずに並び替えるソートサブルーチン。
$aと$bの値を変更してしまうと、元の値も変わってしまうので、注意が必要。

ハッシュを値によってソートする

my @keys = sort { $hash{$b} <=> $hash{$a}} keys %hash;

こうすれば、値を利用してソートすることができる。
もちろんソートできるのは、keysやvaluesが返すリストであって、ハッシュそのものではない。

複数のキーでソートする

論理OR演算子を使うと、複数のキーでソートすることができる。

my @keys = sort {
    $hash{$b} <=> $hash{$a} or
    $a cmp $b
} keys %hash;

まず値を数値として比較する。大きさが違えば<=>は1か-1を返すので、そこで比較は終了。値が同じであれば0を返すので、cmpによるキーの比較が行なわれる。

substrを使って部分文字列をいじる

substr(文字列, 開始位置, 文字数);

substr関数は文字列の中から、一部を取り出す。文字数を省略した場合は、開始位置から末尾までを取り出す。開始位置に負の数を指定した場合は、後ろから○文字目を開始位置とする。

my $part = substr("abcdefg", 2, 3);  # cdeを返す
my $part = substr("abcdefg", 4);     # efgを返す
my $part = substr("abcdefg", -6, 3); # bcdを返す


substrの第一引数が変数の場合、指定した部分だけを置き換えることができる。

my $str = "That's bad.";
substr($str, 7, 3) = "good"; # $strは"That's good"になる。

$strは"That's good"になる。


4番目の引数として置き換え文字列を指定することもできる。

substr($str, index($str, "b"), 3, "good");

これは上と全く同じ。開始位置をindexで指定している。

indexを使って部分文字列を探す

index(文字列, 部分文字列, 開始位置)

index関数は文字列内で部分文字列が最初に現れる位置を返す。開始位置を指定すると、それより後ろから部分文字列を探す。

my $word = "abcdeabcde";
my $w1 = index($word, "a");   # $w1は0
my $w2 = index($word, "a", 3) # $w2は5


一番後ろにある部分文字列の位置を知りたい場合は、rindex関数を使う。開始位置を指定した場合は、それより前で最後に現れる部分文字列の位置を返す。

my $word = "abcdeabcde";
my $w1 = index($word, "a");   # $w1は5
my $w2 = index($word, "a", 3) # $w2は0

プロセスをファイルハンドルとして使う

逆クォートを使ってコマンドからの入力を読み込む場合は、子プロセスが終了するまで、Perlプロセスは待たなければならない。並立実行する子プロセスを起動するには、コマンドの前後どちらかに縦棒を付けたものを指定する。

open DATE, "date|"; # dateコマンドの結果がDATEに送られる
open MAIL, "|mail"; # MAILに書き出したものがmailコマンドに送られる

後ろにパイプ(縦棒)を付けると入力用に、後ろにパイプを付けると出力用にオープンされる。これらをパイプオープンと呼ぶこともある。子プロセスが作れなかったらopen演算子は偽を返す。コマンドそのものが存在しなかったり、コマンドがエラーで終了した場合は、close演算子を使った時に、エラーが出る。


使い方は普通のファイルハンドルと同じ。

my $date = <DATE>;
print MAIL "hoge hoge\n";


入力用のファイルハンドルに連結したプロセスが終了すると、ファイルの終わりを返す。出力用のファイルハンドルに連結したファイルハンドルをクローズすると、Perlはプロセスが終了ステータスを返すのを待つ。終了ステータスは$?変数にセットされ、その値はsystem関数が返すものと同じ。


パイプオープンを使うと、結果が得られるたびに処理していくということができる。時間がかかるコマンドの場合は、パイプオープンを使った方が、少しずつでも結果が表示されるので、いらいらしないで済むと思う。