さて今回は『かんたんPerl』では書ききれなかったPerlの機能ひとつ、組み込み関数mapについて書きます。

Karte Pomponius Mela
 プログラミングによる自動化というと思い出すのが、大量のデータに同じ処理を当てはめて大量の答えを出すという反復処理(イテレーション)が思い浮かびます。リストにデータを入れて、それを加工して返すのが典型的ですね。リストの反復処理は、『かんP』のやり方ではfor(foreach)を使ってやっていました。たとえば1から10までの数の平方根を求めるプログラムならこうなります。
#! /usr/bin/perl
#
# forSqrt.pl -- forを使って平方根を反復的に求める

use 5.010;
use strict;
use warnings;

my @from = (1..10);
my @to = ();

for my $from (@from) {
   push @to, sqrt($from);
}

say for @to;
 実行します。
[sample]$ ./forSqrt.pl
1
1.4142135623731
1.73205080756888
2
2.23606797749979
2.44948974278318
2.64575131106459
2.82842712474619
3
3.16227766016838
[sample]$
 まあそうなりますね。これをmapで書き換えるとこうなります。
#! /usr/bin/perl
#
# mapSqrt.pl -- forを使って平方根を反復的に求める

use 5.010;
use strict;
use warnings;

my @from = (1..10);
my @to = map { sqrt ($_) } @from;

say for @to;
 結果は同じです。
[sample]$ ./mapSqrt.pl
1
1.4142135623731
1.73205080756888
2
2.23606797749979
2.44948974278318
2.64575131106459
2.82842712474619
3
3.16227766016838
[sample]$
 ということで、mapは
 @戻り値のリスト = map { 「@引数のリスト」の各要素を$_に入れた処理 } @引数のリスト;
という構文でforを書き直せるものです。
my @to = ();
と、わざわざ@toを宣言して空リストで宣言しなくていいのがうれしいですね。
 mapは、配列を別の配列に、または、ハッシュに変換するときに便利です。ハッシュへの変換の例を挙げます。『かんP』では、リストの中に重複する要素をまとめるために、ハッシュを使いました。以下のプログラムは、1月から12月までの月の日数のリストをサマライズし、一年には何日の月があるかを調べています。
#! /usr/bin/perl
#
# monthDays.pl -- 月の日数の分類(forとハッシュ)

use 5.010;
use strict;
use warnings;

my @mDays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
my %mSummary = ();

for my $mDay (@mDays) {
   $mSummary{$mDay} = 1;
}

my @mSorted = sort {$a <=> $b} keys %mSummary;
my $mNumber = scalar @mSorted;
say "月の日数は @mSorted の $mNumber 種類があります。";
 実行します。
[sample]$ ./monthDays.pl
月の日数は 28 30 31 の 3 種類があります。
[sample]$
 こういう、ハッシュを使ってリストの要素をサマライズするという処理は、わりとしょっちゅうやりがちです。この1という値に意味はなくて、要するにハッシュの機能を使ってもとのリスト@mDaysがサマライズされて28, 30, 31という3値のリストに変換されるからで、別に2でも100でも文字列"unique"でもいいと思います。でもこんなところで個性を発揮してもしょうがないので特に希望がなければ素直に1と使いましょう。
 さて、この同じプログラムをmapでやってみます。
#! /usr/bin/perl
#
# monthDays2.pl -- 月の日数の分類(mapとハッシュ)

use 5.010;
use strict;
use warnings;

my @mDays = (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
my %mSummary = map {($_, 1)} @mDays;
my @mSorted = sort {$a <=> $b} keys %mSummary;
my $mNumber = scalar @mSorted;
say "月の日数は @mSorted の $mNumber 種類があります。";
 実行します。
[sample]$ ./monthDays2.pl
月の日数は 28 30 31 の 3 種類があります。
[sample]$
 @mDaysの各要素に対して($_, 1)という2値のリストを返す手続きを書きます。つまり、mapを使って配列の要素数を増やすこともできます。これで左辺の@mdaysは各々の値(月の日数)と1が交互にくるリスト
 31, 1, 28, 1, 31, 1, 30, 1, 31, 1, 30, 1, 31, 1, 31, 1, 30, 1, 31, 1, 30, 1, 31, 1
になります。あとはこれをドバーンとハッシュ%mSummaryに代入することで、日数がキーに、1が値になるハッシュに変換されます。これも、%mSummaryを、わざわざ前もってmy宣言、空リスト()で初期化しなくて済むのがいいですね。
 逆に、単純に繰り返し処理をするとき(リストやハッシュに変換しなくていいとき)は、変にmapを使わないで素直にforを使った方がいいです。

 さて、少し『かんたんPerl』の範囲を逸脱しますが、monthDays3.plは無名ハッシュを使うと数珠つなぎ化できます。
#! /usr/bin/perl
#
# monthDays3.pl -- 月の日数の分類(mapとハッシュ、数珠つなぎ化)

use 5.010;
use strict;
use warnings;

my @mSorted = sort {$a <=> $b} keys { map {($_, 1)} (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) };
my $mNumber = scalar @mSorted;
say "月の日数は @mSorted の $mNumber 種類があります。";
 無名ハッシュはハッシュへのリファレンスを返すもので、
{ キー1, 値1, キー2, 値2… }
のように書きます。これでメモリー空間上に名前のないハッシュができ、そのリファレンス(Cのポインターのようなもの)が返ります。
$スカラー = { キー1, 値1, キー2, 値2… }
のように書くと、無名ハッシュのリファレンスが$スカラーに入るので、
$スカラー->{キー1}
のように引くと、値1が返ります。
 で、keys関数は引数にハッシュ変数だけではなくて無名ハッシュを取ることもできるので、map関数を{}で囲んで無名ハッシュを作り、それをkeys関数に渡すことができます。上のプログラムではさらにkeys関数の結果リストをsort関数に渡しています。
 無名ハッシュやリファレンスについて興味が湧いた人はperldoc perlrefを見るか、拙著『すぐわかるオブジェクト指向Perl』を読んでください。期せずして宣伝スミマセン。

perlref - Perlのリファレンスとネストしたデータ構造 - perldoc.jp