続・初めてのPerl 第5章 「リファレンスとスコープ」

練習問題の回答をメモ。

ex05-1

ハッシュを作るつもりでハッシュリファレンスを作ってしまっているのが問題。実行してみるまで気がつかなかった。
use warningsプラグマを常に使おう。
「% perldoc perldiag」で、Perl が生成する可能性のあるすべてのメッセージの詳細が見れるよ。

ex05-2
#!/usr/bin/perl
use strict;

my %total_bytes;
my %source_total_bytes;
while (<>) {
  next if /^#/;
  my ($source, $destination, $bytes) = split;
  $total_bytes{$source}{$destination} += $bytes;
  $source_total_bytes{$source} += $bytes;
}

# ハッシュを値によってソートする 初めてのPerl p262
foreach my $source (sort { $source_total_bytes{$b} <=> $source_total_bytes{$a} } keys %source_total_bytes) {
  foreach my $destination (sort { $total_bytes{$source}{$b} <=> $total_bytes{$source}{$a} } keys %{ $total_bytes{$source} }) {
    print "$source => $destination: $total_bytes{$source}{$destination} bytes\n";
  }
  print "$source machine's total byte: $source_total_bytes{$source}\n";
  print "\n";
}

解答見たら、ソース(source)ごとの送信バイト数の合計値を計算するのに新しい「my %source_total_bytes;」なんていうハッシュを用意しないで、「すべての destination」を表すキー($all)を作って以下のように利用していた。これは勉強になりました。

my $all = "**all machines**";
$total_bytes{$source}{$all} += $bytes;

$all を使って書き直すと、以下のようになる。

#!/usr/bin/perl
use strict;

my %total_bytes;
my $all = "**all machines**";

while (<>) {
  next if /^#/;
  my ($source, $destination, $bytes) = split;
  $total_bytes{$source}{$destination} += $bytes;
  $total_bytes{$source}{$all} += $bytes;
}

# ハッシュを値によってソートする 初めてのPerl p262
foreach my $source (sort { $total_bytes{$b}{$all} <=> $total_bytes{$a}{$all} } keys %total_bytes) {
  foreach my $destination (sort { $total_bytes{$source}{$b} <=> $total_bytes{$source}{$a} } keys %{ $total_bytes{$source} }) {
    next if $destination eq $all;
    print "$source => $destination: $total_bytes{$source}{$destination} bytes\n";
  }
  print "$source: $total_bytes{$source}{$all} total bytes sent\n";
  print "\n";
}

反省点

同じデータ構造にまとめるべきデータは何か考えて、どうやってそれを実現できるかまでちゃんと考えよう。今回の練習問題の ex05-2 だと、ハッシュ「%source_total_bytes」は必ずしも必要なかったね。