今日も「かんたんPerl補遺」として、本書では紹介しきれなかった関数を紹介します。前回(Opus 9)ではリストをリストに変換するmap関数を研究しました。
復習すると、
のような処理は、
のように書き換えられます。
短く、スッキリしますし、数珠つなぎできるのでラクチンです。
リストをリストに変換するのはmapと覚えましょう。
さて、grepはmapと似ていますが、リストから値を抽出してサブセットのリストを作る関数です。

のような処理は、
のように書き換えることが出来ます。つまり、関数が真を返すリスト要素のみを返します。
『かんたんPerl』の第11章では、AKB総選挙の「恋するフォーチュンクッキー」選抜の中から、名前が3文字のメンバーを、正規表現を使って以下のように抽出していました。
実行するとこうなります。
キモはこの部分です。
苗字と名前の間には(このデータで唯一)スペースがあり、名前が3文字ということはスペースとタブの間に任意の文字「.」が3個あるということなので、そのデータにマッチする正規表現は「 ...\t」となります。これが真のとき、データをprintしています。
ここでは、whileを使ってDATAファイル ハンドルのデータを取得しています。
この場合<DATA>は、スカラー コンテキストで評価されます。
さて、このプログラムをgrepで書きなおしてみます。
見た目がスッキリしましたね。
この場合、<DATA>はリスト コンテキストで評価され、各々のリスト要素についてコード ブロックが評価され、真を返すリスト要素のみから出来たリスト要素が返されるので、それをprintしています。
名前が3文字のメンバーの、名前とメール アドレスだけを抽出してみましょう。(念のため、このメール アドレスは、こういう解説文を書くときに使うようにRFC 2606で定められているドメイン名example.comを使っているので、誰にも迷惑は掛かりません。)
while版はこうなります。
grep版は、mapを数珠つなぎしてみます。
若干可読性が落ちた気がしますが、実行してみます。
バッチリですね。
Perlはmap、grepの他に、sortも手続きを引数として取ります。
これを高階関数といい、いま流行の関数型プログラミングのもとになっている考え方です。
復習すると、
my @出力リスト = ();
for (@入力リスト) {
push @出力リスト, 関数($_);
}
for (@入力リスト) {
push @出力リスト, 関数($_);
}
のような処理は、
my @出力リスト = map {関数($_)} @入力リスト;
のように書き換えられます。
短く、スッキリしますし、数珠つなぎできるのでラクチンです。
リストをリストに変換するのはmapと覚えましょう。
さて、grepはmapと似ていますが、リストから値を抽出してサブセットのリストを作る関数です。

my @出力リスト = ();
for (@入力リスト) {
if (関数($_)) {
push @出力リスト, $_;
}
}
for (@入力リスト) {
if (関数($_)) {
push @出力リスト, $_;
}
}
のような処理は、
my @出力リスト = grep {関数($_)} @入力リスト;
のように書き換えることが出来ます。つまり、関数が真を返すリスト要素のみを返します。
『かんたんPerl』の第11章では、AKB総選挙の「恋するフォーチュンクッキー」選抜の中から、名前が3文字のメンバーを、正規表現を使って以下のように抽出していました。
#! /usr/bin/perl
#
# matchDot.pl -- 名前が3文字のメンバー(ドット(.)による検索)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
while (<DATA>) {
print if / ...\t/; # スペースとタブの間に3文字
}
__DATA__
1 指原 莉乃 Rino_Sashihara@example.com Team-H HKT48
2 大島 優子 Yuuko_Ohshima@example.com Team-K AKB48
3 渡辺 麻友 Mayu_Watanabe@example.com Team-B AKB48
4 柏木 由紀 Yuki_Kashiwagi@example.com Team-B AKB48
5 篠田 麻里子 Mariko_Shinoda@example.com Team-A AKB48
6 松井 珠理奈 Jurina_Matsui@example.com Team-E SKE48
7 松井 玲奈 Rena_Matsui@example.com Team-S SKE48
8 高橋 みなみ Minami_Takahashi@example.com Team-A AKB48
9 小嶋 陽菜 Haruna_Kojima@example.com Team-A AKB48
10 宮澤 佐江 Sae_Miyazawa@example.com Team-K AKB48
11 板野 友美 Tomomi_Itano@example.com Team-K AKB48
12 島崎 遥香 Haruka_Shimazaki@example.com Team-B AKB48
13 横山 由依 Yui_Yokoyama@example.com Team-A AKB48
14 山本 彩 Sayaka_Yamamoto@example.com Team-N NMB48
15 渡辺 美優紀 Miyuki_Watanabe@example.com Team-M NMB48
16 須田 亜香里 Akari_Suda@example.com Team-KII SKE48
#
# matchDot.pl -- 名前が3文字のメンバー(ドット(.)による検索)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
while (<DATA>) {
print if / ...\t/; # スペースとタブの間に3文字
}
__DATA__
1 指原 莉乃 Rino_Sashihara@example.com Team-H HKT48
2 大島 優子 Yuuko_Ohshima@example.com Team-K AKB48
3 渡辺 麻友 Mayu_Watanabe@example.com Team-B AKB48
4 柏木 由紀 Yuki_Kashiwagi@example.com Team-B AKB48
5 篠田 麻里子 Mariko_Shinoda@example.com Team-A AKB48
6 松井 珠理奈 Jurina_Matsui@example.com Team-E SKE48
7 松井 玲奈 Rena_Matsui@example.com Team-S SKE48
8 高橋 みなみ Minami_Takahashi@example.com Team-A AKB48
9 小嶋 陽菜 Haruna_Kojima@example.com Team-A AKB48
10 宮澤 佐江 Sae_Miyazawa@example.com Team-K AKB48
11 板野 友美 Tomomi_Itano@example.com Team-K AKB48
12 島崎 遥香 Haruka_Shimazaki@example.com Team-B AKB48
13 横山 由依 Yui_Yokoyama@example.com Team-A AKB48
14 山本 彩 Sayaka_Yamamoto@example.com Team-N NMB48
15 渡辺 美優紀 Miyuki_Watanabe@example.com Team-M NMB48
16 須田 亜香里 Akari_Suda@example.com Team-KII SKE48
実行するとこうなります。
[sample]$ ./matchDot.pl
5 篠田 麻里子 Mariko_Shinoda@example.com Team-A AKB48
6 松井 珠理奈 Jurina_Matsui@example.com Team-E SKE48
8 高橋 みなみ Minami_Takahashi@example.com Team-A AKB48
15 渡辺 美優紀 Miyuki_Watanabe@example.com Team-M NMB48
16 須田 亜香里 Akari_Suda@example.com Team-KII SKE48
5 篠田 麻里子 Mariko_Shinoda@example.com Team-A AKB48
6 松井 珠理奈 Jurina_Matsui@example.com Team-E SKE48
8 高橋 みなみ Minami_Takahashi@example.com Team-A AKB48
15 渡辺 美優紀 Miyuki_Watanabe@example.com Team-M NMB48
16 須田 亜香里 Akari_Suda@example.com Team-KII SKE48
キモはこの部分です。
while (<DATA>) {
print if / ...\t/; # スペースとタブの間に3文字
}
print if / ...\t/; # スペースとタブの間に3文字
}
苗字と名前の間には(このデータで唯一)スペースがあり、名前が3文字ということはスペースとタブの間に任意の文字「.」が3個あるということなので、そのデータにマッチする正規表現は「 ...\t」となります。これが真のとき、データをprintしています。
ここでは、whileを使ってDATAファイル ハンドルのデータを取得しています。
この場合<DATA>は、スカラー コンテキストで評価されます。
さて、このプログラムをgrepで書きなおしてみます。
#! /usr/bin/perl
#
# matchDotGrep.pl -- 名前が3文字のメンバー(ドット(.)による検索、grep版)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
print grep { / ...\t/ } <DATA>;
__DATA__
…以下略…
#
# matchDotGrep.pl -- 名前が3文字のメンバー(ドット(.)による検索、grep版)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
print grep { / ...\t/ } <DATA>;
__DATA__
…以下略…
見た目がスッキリしましたね。
この場合、<DATA>はリスト コンテキストで評価され、各々のリスト要素についてコード ブロックが評価され、真を返すリスト要素のみから出来たリスト要素が返されるので、それをprintしています。
名前が3文字のメンバーの、名前とメール アドレスだけを抽出してみましょう。(念のため、このメール アドレスは、こういう解説文を書くときに使うようにRFC 2606で定められているドメイン名example.comを使っているので、誰にも迷惑は掛かりません。)
while版はこうなります。
#! /usr/bin/perl
#
# matchDotNM.pl -- 名前が3文字のメンバーの名前とメール アドレスだけを返す
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
while (<DATA>) { # スペースとタブの間に3文字
if (/ ...\t/) {
my (undef, $name, $mail) = split /\t/;
say "$name $mail";
}
}
__DATA__
…後略…
#
# matchDotNM.pl -- 名前が3文字のメンバーの名前とメール アドレスだけを返す
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
while (<DATA>) { # スペースとタブの間に3文字
if (/ ...\t/) {
my (undef, $name, $mail) = split /\t/;
say "$name $mail";
}
}
__DATA__
…後略…
grep版は、mapを数珠つなぎしてみます。
#! /usr/bin/perl
#
# matchDotNMf.pl -- 名前が3文字のメンバーの名前とメール アドレスだけを返す(mapとgrep)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
print map { join("\t", (split "\t")[1, 2])."\n" } grep { / ...\t/ } <DATA>;
__DATA__
…後略…
#
# matchDotNMf.pl -- 名前が3文字のメンバーの名前とメール アドレスだけを返す(mapとgrep)
use 5.010;
use strict;
use warnings;
use utf8;
if ($^O eq "MSWin32") {
binmode STDOUT, ":encoding(Shift_JIS)";
} else {
binmode STDOUT, ":encoding(UTF-8)";
}
print map { join("\t", (split "\t")[1, 2])."\n" } grep { / ...\t/ } <DATA>;
__DATA__
…後略…
若干可読性が落ちた気がしますが、実行してみます。
[sample]$ ./matchDotNMf.pl
篠田 麻里子 Mariko_Shinoda@example.com
松井 珠理奈 Jurina_Matsui@example.com
高橋 みなみ Minami_Takahashi@example.com
渡辺 美優紀 Miyuki_Watanabe@example.com
須田 亜香里 Akari_Suda@example.com
[sample]$
篠田 麻里子 Mariko_Shinoda@example.com
松井 珠理奈 Jurina_Matsui@example.com
高橋 みなみ Minami_Takahashi@example.com
渡辺 美優紀 Miyuki_Watanabe@example.com
須田 亜香里 Akari_Suda@example.com
[sample]$
バッチリですね。
Perlはmap、grepの他に、sortも手続きを引数として取ります。
これを高階関数といい、いま流行の関数型プログラミングのもとになっている考え方です。