「初めてのPerl」 15章 (文字列処理とソート)

1月25日にOSAKA MUSEで行われるAPOGEEのワンマンライヴの日は会社を休んでやろう!と思ってスケジュールを確認したら、Oracleの研修を受ける予定になってました…。この間、両親が大阪、京都を訪問する時期と重なってしまったのでOracleの研修は延期した(というより申し込まなかった)んだよなぁ…。研修後に行っても普通に観れるかしら。などとすっかりAPOGEEモードのbonlifeです。とりあえず、11月27日は定時退社して2nd LINEへ!
そんなこんなでPerlのお勉強です。「初めてのPerl」の15章は「文字列処理とソート」。15章の内容はだいたい以下のような感じです。

  • index (部分文字列の位置を返す)
  • substr (部分文字列をいじる)
  • sprintf (フォーマットした文字列を変数にしまったり)
  • 高度なソート (ハッシュを値でソートしたり、複数のキーでソートしたり)

このあたりは比較的とっつきやすい内容ですね。ハッシュのソートは慣れないとちょっと難しい気がしますが、なんとかなりそうです。ということで練習問題のbonlife的(not 模範)解答例です。
ex15-1.pl

  • 数値のリストを読み込んで、それを数値としてソートした上で、右寄せで表示するプログラムを書く
  • numbersというファイルを使う (内容は以下の通り)
17 1000 04 1.50 3.14159
-10 1.5 4 2001 90210 666
9 0 2 1 0
2001 42 -40 98.6 2.71828
#! perl
use strict;
use warnings;

my @num_list;
while (<>) {
    chomp;
    push @num_list, split(/\s/,$_);
}
foreach (sort { $a <=> $b } @num_list) {
    printf "%20s\n", $_;
}

出力結果は以下の通り。

ex15-1.pl numbers
                 -40
                 -10
                   0
                   0
                   1
                1.50
                 1.5
                   2
             2.71828
             3.14159
                  04
                   4
                   9
                  17
                  42
                98.6
                 666
                1000
                2001
                2001
               90210

解答例を見て気づいたことは以下の通り。

  • splitはデフォルトでスペースで区切ってくれるので、 $_ を処理するのであれば split だけで十分
  • printfのフォーマット指定で s ではなく、 g でも良い (その場合、前ゼロや小数部の末尾ののゼロが消える)

考え方は合ってましたが、細かい部分の書き方がまだまだダサいですね。こざっぱりしたコードを書けるようになりたいです。続いて2問目。
ex15-2.pl

  • ハッシュのデータを、姓のアルファベット順(大文字と小文字は区別しない)でソートして表示するプログラムを書く
  • 姓が同じ場合には、名(大文字と小文字は区別しない)でソート
#! perl
use strict;
use warnings;

my %last_name = qw{ 
	fred flintstone Wilma Flintstone Barney Rubble
	betty rubble Bamm-Bamm Rubble PEBBLES FLINTSTONE
};

my @keys = sort {
    "\L$last_name{$a}" cmp "\L$last_name{$b}" or
    "\L$a"             cmp "\L$b"
} keys %last_name;
foreach my $key (@keys) {
    printf "%-10s %-10s\n",$key ,$last_name{$key};
}

出力結果は以下の通り。

ex15-2.pl
fred       flintstone
PEBBLES    FLINTSTONE
Wilma      Flintstone
Bamm-Bamm  Rubble
Barney     Rubble
betty      rubble

解答例を見て気づいたことは特になし。ほぼ同じでした。続いて3問目。
ex15-3.pl

  • ユーザから与えられた文字列の中から、与えられた部分文字列が現れる場所をすべて探し出して、その位置を表示するプログラムを書く
#! perl
use strict;
use warnings;

print "Please input some string      : ";
chomp (my $value = <STDIN>);
print "Please input some part string : ";
chomp (my $part  = <STDIN>);

my $where = index($value, $part);
my @position;

while ($where != -1) {
    push @position, $where;
    $where = index($value, $part,$where + 1);
}

if (@position) {
    print "Matched position(s) : @position\n";
} else {
    print "Not matched.\n";
}

出力結果は以下の通り。

ex15-3.pl
Please input some string      : This is a test.
Please input some part string : t
Matched position(s) : 10 13

解答例を見て気づいたことは以下の通り。

  • $whereがwhileループの外に出てしまっているのはスコープの面であまりよくなさそう
  • forループに初期値だけ与えて、lastを使ってループを抜けるとスッキリ
  • while(1)などを使っても良い

$whereの使い方がイマイチだなぁ、と思っていたら、なるほどな解説でしたよ。勉強になります。(最初、indexが3つ目の引数取れることを忘れていて、substrで文字列を切り取ったりするコードを書きかけていたのは秘密です。)
いよいよ「初めてのPerl」の終わりが近づいてきましたよ。あと2章!今週中には終わらせられそうです。細々と頑張りますよっ。