Perl入学式2013年度第5回補講・参加レポート vol.4

年明け1/11に行われた「Perl入学式第5回in東京補講」のレポート第4回です。

今日は前回自分で考えたmap, grepの練習問題への回答に対する、校長の答えを突き合わせながら見てみます。
前回の記事はこちら。
http://note103.hateblo.jp/entry/2014/02/04/003829

僕の回答はこんなでした。

#!/usr/bin/env perl
use strict;
use warnings;

my @files = qw/papix.pl moznion.pm macopy.py boolfool.vim/;

sub maps {
    my @array1 = @_;
    my @result1 = map {$_ . ".bak"} @array1;
        return "@result1";
}

sub greps {
    my @array2 = @_;
    my @result2 = grep {$_ =~ /p[lm]$/} @array2;
        return "@result2";
}

print maps (@files)."\n";
print greps (@files)."\n";

実行すると、こうなります。

papix.pl.bak moznion.pm.bak macopy.py.bak boolfool.vim.bak
papix.pl moznion.pm

対して、校長の回答。まずmapから。

#!/usr/bin/env perl
use strict;
use warnings;
use DDP;

my @files = qw/papix.pl moznion.pm macopy.py boolfool.vim/;

sub map_bak {
    my $array_ref = shift;
    my @result = map {$_ . '.bak'} @{$array_ref};
    return \@result;
}

my $map_bak = map_bak(\@files);
p $map_bak;

ええと、いきなりカルチャーショック的に違ったのが、素材の配列@filesを、そのまま引数として受け取るのではなく「配列リファレンス」にして受け取ってることでした。
これについて、校長は「リファレンスで受け取ってリファレンスで返すことにしましょう。この辺は自由で、配列で受け取って配列で返してもいいですが、基本的には、返し方はどちらでもよいとしても、受け取る方はリファレンスを使うのが鉄板ですかね」とのことでした。
そうなんや・・

#ちなみに、僕のと関数名が違いますが、これは僕の方が勝手に省略したやつだからで、問題に正確に答えているのは校長の方です。念のため。

実行すると、こうです。

\ [
    [0] "papix.pl.bak",
    [1] "moznion.pm.bak",
    [2] "macopy.py.bak",
    [3] "boolfool.vim.bak"
]

で、ちょっと省略させて、@resultを省いたのがこの形。

#!/usr/bin/env perl
use strict;
use warnings;
use DDP;

my @files = qw/papix.pl moznion.pm macopy.py boolfool.vim/;

sub map_bak {
    my $array_ref = shift;
    return [ map {$_ . '.bak'} @{$array_ref} ];
}

my $map_bak = map_bak(\@files);
p $map_bak;

なるほどー・・。
いずれにしても、配列リファレンスで受け取りつつ、mapの結果も配列リファレンスとして返している、ということですね。くり返しですが、配列で受け取るのもアリ、だそうです。

ちなみに、ここでもDDPに関するQ&Aが軽く展開されました。(最後のpという出力のやつ)
DDPについては以下でも軽く書きましたが、
http://note103.hateblo.jp/entry/2013/10/22/014956
ようは「Data::Dumperの豪華版みたいな」だそうでした。

では次、grep。校長の回答はこのようでした。

#!/usr/bin/env perl
use strict;
use warnings;
use DDP;

my @files = qw/papix.pl moznion.pm macopy.py boolfool.vim/;

sub grep_pl_and_pm {
    my $array_ref = shift;
    return [ grep {$_ =~ /\.p[lm]$/} @{$array_ref} ] ;
}

my $grep_pl_and_pm = grep_pl_and_pm (\@files);
p $grep_pl_and_pm;

実行はこんな。

\ [
    [0] "papix.pl",
    [1] "moznion.pm"
]

なるほどー・・。
で、ここでサポーターさんからのひとくちコメントで、grepの場合は「$_ =~ 」なくてもいける、とのお話が。つまりこれでもよいそうです。

    return [ grep { /\.p[lm]$/ } @{$array_ref} ] ;

ただ初心者に教える場合は、それだとちょっと何やってるかわかりづらいかもね、とのことでしたので、それこそ各自好みに合わせて、というところでしょうか。

ちなみに、僕の回答だと、このmap関数とgrep関数を並べて書いたとき、わざわざ「@array, @array2」みたいに、式で使う変数名をもう片方の関数とかぶらないようにつけていましたが、校長が並べて書くのを見ていたら、気にせず同じ「$array_ref」を使っていたので、それも小さな気づきでした。
異なる関数内で使う変数は、別に同じ名前で良かったのか、と。
一応、まとめ直したコードをGistに上げてみました。
https://gist.github.com/note103/8909409

ということで、map, grepに関する練習問題は以上で、次の記事ではwhile文などを使った練習問題について進めたいと思います。
https://github.com/perl-entrance-org/workshop-2013-05/blob/master/slide.md#%E7%B7%B4%E7%BF%92%E5%95%8F%E9%A1%8C-1

ところで、前回、mapとgrepについて扱った段階で、2点漏れがありました。
ひとつは、資料によればmapは以下の2種類の書き方がある、ということだったのですが。

1. map BLOCK LIST形式
例: map { $_ * 2; } @array1;

2. map EXPR, LIST形式
例: map $_ * 2, @array1;

基本的には1のブロック形式の方を良く使うよ、みたいな話だったのを思い出しました。直感的には、pushみたいに使える後者の方が普通かなと思ってたのですが。

あと、その際には{ }内の最後にあるセミコロン、これは、括弧内の式が一つだけなら無くてもいいよ、と教えてもらいました。
なので、前回の僕の回答も、

(前略)
sub maps {
    my @array1 = @_;
    my @result1 = map {$_ . ".bak"} @array1;
    return "@result1";
}
(後略)

という風に、セミコロン入れてません。(校長のもそうなってる)

もうひとつ補足。たしかこの話をしている前後で、サポーターで参加してくださっている@ytnobodyさん(あずまさん)が、以前にブログでmapのこと書いたので良かったら参照してみてください、的なことを言われていた気がするのですが、いろいろ探してみてもちょっとわからなかったので追ってチェックしてみたいと思っています。
あとで直接聞くかもしれませんが、備忘録がわりに書いておきました。

最後に蛇足ですが、前回の記事では、map, grepを使う練習なのにボーっとして普通にfor文で書いたらそれすら全然動かなくてガックリ来たので、一応書きなおしてみました。
前回は、こんな風に書いて・・

#!/usr/bin/env perl
use strict;
use warnings;

my @files = qw/papix.pl moznion.pm macopy.py boolfool.vim/;

sub maps {
    my @array = @_;
    for my $array (@array) {
        return $array.".bak";
    }
}

sub greps {
    my @array2 = @_;
    for my $array2 (@array2) {
        if ($array2 =~ /p[ml]$/) {
            return $array2;
        }
    }

}

print maps (@files)."\n";
print greps (@files)."\n";

実行したらこんなでしたが・・

papix.pl.bak
papix.pl

とりあえずこうやれば回答自体は出たなと。

#!/usr/bin/env perl
use strict;
use warnings;

my @files = qw/papix.pl moznion.pm macopy.py boolfool.vim/;
my (@result1, @result2);

sub maps {
    my @array = @_;
    for my $array (@array) {
        push @result1, $array.".bak";
    }
return "@result1";
}

sub greps {
    my @array = @_;
    for my $array (@array) {
        if ($array =~ /p[ml]$/) {
            push @result2, $array;
        }
    }
return "@result2";
}

print maps (@files)."\n";
print greps (@files)."\n";

実行!

papix.pl.bak moznion.pm.bak macopy.py.bak boolfool.vim.bak
papix.pl moznion.pm

はい。まあ、上の正答編を見た後だとかなりナンですが・・

以上です!!