第15章 文字列処理とソート 練習問題 1,2

ゆかりライブの興奮冷めやらぬ中、本日もPerl。

1. 数値のリストを読み込んでソートし、右寄せで表示するプログラム

数値のリストはオライリーからダウンロードできるサンプルコードに入ってるのを使いました。numbersというファイルです。oreilly.co.jp -- Online Catalog: 初めてのPerl 第3版から、どうぞ。といっても、たいした内容でもないので以下に引用。

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

さて、これを一行ごとに読み込んで数値としてソートします。

#!/usr/bin/env perl -w
use strict;

open NUMS, 'numbers' or die "Can't open the file: $!";

while (<NUMS>) { # 一行ごとに読み込みます
    chomp;
    print (('-' x 6) . "\n"); # 行ごとの区切りを表示

    # 入力行を分割し数値順でソート
    my @numbers = sort {$a <=> $b} split; 

    foreach (@numbers) {
        printf "%6d\n", $_; # 右寄せ表示
    }
}

close NUMS;

ソートに数値比較用のブロックである、{$a <=> $b}を渡しています。実行すると、以下のような感じ。

$ ./ex15-1.pl
------
     1
     3
     4
    17
  1000
------
   -10
     1
     4
   666
  2001
 90210
#以下略

解答とは割と雰囲気が違うコードになってしまいましたが、やってることは同じようなので良いことにしますよ。

2. ハッシュがからんだ複雑なソートを行うプログラム

もちょっとくわしく問題を説明。まず、以下のようなハッシュが有ります。(問題に掲載されていたものをわかりやすく整形)

my %last_name = (
    'fred' => 'flintstone',
    'Wilma' =>  'Flintstone',
    'Barney' => 'Rubble',
    'betty' => 'rubble',
    'Bamm-Bamm' => 'Rubble',
    'PEBBLES' => 'FLINTSTONE',
);

これは人名のハッシュで名前をキーに姓を記録しています。

このハッシュの内容をソートして表示するのが問題です。ソートの規則はまず、姓を用いてアルファベット順(大文字小文字無視)でソートします。姓が同じ場合は、名前を用いてアルファベット順(大文字小文字無視)にソートします。

で、これを実現するにはsortに適切なサブルーチンを渡してやる必要があります。以下のようにやってみました。

#!/usr/bin/env perl -w
use strict;

# 問題のハッシュ
# last_nameのキーはfirst_name
my %last_name = (
    'fred' => 'flintstone',
    'Wilma' =>  'Flintstone',
    'Barney' => 'Rubble',
    'betty' => 'rubble',
    'Bamm-Bamm' => 'Rubble',
    'PEBBLES' => 'FLINTSTONE',
);

# 名字で比較して、名字が同じ時は名前で比較します
sub by_lastname_firstname {
    if    ("\L$last_name{$a}" lt "\L$last_name{$b}") { 
        return -1;
    } 
    elsif ("\L$last_name{$a}" gt "\L$last_name{$b}") {
        return 1;
    }
    else {   # 名字が同じ時は
        return "\L$a" cmp "\L$b"; # 名前で比較します
    }
}

# 表示します
foreach (sort by_lastname_firstname keys %last_name) { 
    print "$_ $last_name{$_}\n";
}

by_lastname_firstnameという名前のサブルーチンがこのソートの規則を決めています。実行結果は、

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

となって、うまく動いているよう。

さて、解答を見てみると、うちが書いたby_lastname_firstnameサブルーチンにあたるコードがかなりクールでした。これをもとにby_lastname_firstnameを書き直してみましたよ。

# 名字で比較して、名字が同じ時は名前で比較します
sub by_lastname_firstname {
    "\L$last_name{$a}" cmp "\L$last_name{$b}"
        or
    "\L$a" cmp "\L$b";
}

う、美しすぎ。

初めてのPerl

初めてのPerl

  • 作者: ランダル・L.シュワルツ,トムフェニックス,Randal L. Schwartz,Tom Phoenix,近藤嘉雪
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2003/05
  • メディア: 単行本
  • 購入: 15人 クリック: 474回
  • この商品を含むブログ (294件) を見る