Perl入学式2013年度第5回補講・参加レポート vol.7(packege/モジュール, テスト, 最終問題)
昨日ふと、今月末の第6回補講に向けて予習記事を書いてみたりしましたが、
http://note103.hateblo.jp/entry/2014/03/23/002233
その勢いで以前に書き進めたまま中断してしまった第5回の内容をまとめておきたいと思います。
その前までの復習記事でMojolicousの導入の他、map, grep, next, last, 後置if/for, whileなどの落穂ひろい的な内容を触れ終えたところで、
http://note103.hateblo.jp/entry/2014/02/18/184136
とはいえ、というかじつはというか第5回の本題的なところはここからです。
といっても、ここで教わったことについては資料を順に読んでいく&話を聞いていくよりない感じもあるので、そのまま練習問題をどう解いたか、という話に行きたいと思います。
package 'PerlEntrance.pm'
最初の練習問題はこれですね。
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-2
- PerlEntranceというpackageの中にtokyoとosakaという名前の, 引数を持たないサブルーチンを作りましょう
- tokyoはpapix!!!, osakaはboolfool!!!という文字列を返す機能を持たせましょう
ということで、今考えながら書いてみますが、こんな感じでしょうか。
#!/usr/bin/env perl use strict; use warnings; package PerlEntrance { sub tokyo { return "papix!!!" } sub osaka { return "boolfool!!!" } } print PerlEntrance::tokyo()."\n"; print PerlEntrance::osaka()."\n";
このまま、講座のときに撮った動画で確認してみます。
・・確認中・・
あってました。では次。
PerlBiginners::perllevel
- PerlBeginnersというモジュール/packageを作ってその中にperllevelというサブルーチンを作りましょう
- perllevelは1から10の整数の引数を取ります.
use PerlBeginners; print PerlBeginners::perllevel(1); # => # レベル1: Perl 関係の書籍や資料を何も読んでいない。(中略)この言語に合うメンタルモデルを持っていないので、Perl の構文をCOBOL とC++ のような他の言語のものとは区別できていない。
- 引数をレベルと解釈して, 上記のように使います.
- Perlレベルは以下のブログに掲載されているので何らかの形でモジュール内に保持しましょう.
ということで、さっそく作ってみます。まずは「PerlBeginners.pm」として以下を作ります。
#!/usr/bin/env perl use strict; use warnings; package PerlBeginners { sub perllevel { my $str = shift; if ($str ==1) { return "レベル1: Perl 関係の書籍や資料を何も読んでいない。Perl がプログラミング言語だということは知っているが、それ以外のことは何も知らない。他人の書いたPerl プログラムを実行できるので、プログラムの一部を編集することでプログラムの動作の一部(出力される文字列の内容など)を変更できることを知っている。プログラムのほかの部分に変更を加えてもなぜうまくいかないのか理解していない。この言語に合うメンタルモデルを持っていないので、Perl の構文をCOBOL とC++ のような他の言語のものとは区別できていない。"; } elsif ($str == 2) { return "レベル2: 基本ブロック構造の構文を理解しているが、JavaScript のような言語に似ているという程度の認識にとどまっている。ブロックがある種のスコープ効果を生じさせるという理解はあるものの、レキシカル変数のことは知らないし、use strict や use warnings に出会ったこともない。条件の意味を変更することができ、基本的な算術演算子と論理演算子条件を使用できる。必要とするすべてのことを、他人の書いたプログラムに若干の変更を加えることで達成できると思っている。" #中略 } elsif ($str == 10) { return "レベル10: Perl のobfuscation コンテストや「golf」コンテスト*3 に参加する。たとえば、普通のプログラマなら1 つのプログラムを丸ごと必要とするような関数を、埋め込みコードを使って単一の正規表現で実装してしまう。Perl コアにパッチを送ったり、新しいメジャーなモジュールを寄贈したりすることで、Perl コミュニティで有名人になるかもしれない。" } } } 1;
ちょっと長いので中略しました。
これを実行する「practice.pl」はこちらで。
#!/usr/bin/env perl use strict; use warnings; use PerlBeginners; print PerlBeginners::perllevel(1)."\n";
ではこれも解答と突き合わせてみます。
・・確認中・・
ちなみにこのpackage編に入ってからはまこぴー先生のレクチャーに切り替わっています。(それまではpapix校長)
・・確認中・・
なるほど・・確認しましたが、上記の回答でもアリではあるものの、別の考え方としてハッシュリファレンスを使ったこのようなものもあるとのこと。(PerlBeginners.pm)
#!/usr/bin/env perl use strict; use warnings; package PerlBeginners { sub perllever { my $str = shift; my $levellist = { 1 => "レベル1: Perl 関係の書籍や資料を何も読んでいない。Perl がプログラミング言語だということは知っているが、それ以外のことは何も知らない。他人の書いたPerl プログラムを実行できるので、プログラムの一部を編集することでプログラムの動作の一部(出力される文字列の内容など)を変更できることを知っている。プログラムのほかの部分に変更を加えてもなぜうまくいかないのか理解していない。この言語に合うメンタルモデルを持っていないので、Perl の構文をCOBOL とC++ のような他の言語のものとは区別できていない。", 2 => "レベル2: 基本ブロック構造の構文を理解しているが、JavaScript のような言語に似ているという程度の認識にとどまっている。ブロックがある種のスコープ効果を生じさせるという理解はあるものの、レキシカル変数のことは知らないし、use strict や use warnings に出会ったこともない。条件の意味を変更することができ、基本的な算術演算子と論理演算子条件を使用できる。必要とするすべてのことを、他人の書いたプログラムに若干の変更を加えることで達成できると思っている。", #後略 }; return $levellist->{$str}; } } 1;
なるほど・・と言う感じで、これなら10回「return」って書かなくてよいですし、何よりリファレンス使えるとやっぱり色々すっきりする感じありますね・・
テスト
最後のトピック、テスト&ぶつかり稽古ですが、テスト&モジュールはそれぞれそこそこクリアできたのですが、そもそものテーマというか根本をちょっと把握できてなくて、解答編でまこぴーさんとmoznionさんが実演してくださったのを見ると、先にテストを書いて、それに合わせてというかコードを書いていく、という順番だったところ、ぼく&パートナーさんは先にコードを書いて、それを追う感じでテストを書いて確認、みたいな感じにしていたので、その辺ちょっと惜しかったなと。面白みがもう一段階味わえてなかったなと、そういう意味では良い経験だったかなとも思いました。
最後に一応、最終問題の問題と回答を載せておきます。
ということで、まずテストの「yapc.t」。
#!/usr/bin/env perl use strict; use warnings; use Test::More; use YAPC; is YAPC::year(), 2014, 'YAPC_year'; is YAPC::month(), 8, 'YAPC_month'; is YAPC::day(), 28, 'YAPC_day'; ok YAPC::is_yet('2014/03/23'), 'YAPC_is_yet_ok'; ok !YAPC::is_yet('2016/04/23'), 'YAPC_is_yet_not_ok'; done_testing();
次に「YAPC.pm」。
#!/usr/bin/env perl use strict; use warnings; package YAPC { sub year { return '2014'; } sub month { return '8'; } sub day { return '28'; } sub is_yet { my $str = shift; if (defined $str) { if ($str =~ /(\d{4})\/(\d{2})\/(\d{2})/) { if ($1 < 2014) { return 'Yet!'; } elsif ($1 == 2014 && $2 < 8) { return 'Yet!'; } elsif ($1 == 2014 && $2 == 8 && $3 <= 28) { return 'Yet!'; } else { return 0; } } else { return 0; } } else { return undef; } } } 1;
はい。これ途中で「defined ~」というif文を入れていますが、初めこれナシでやったら
Use of uninitialized value in pattern match 〜
というエラーが出たのでいろいろ検索してこれを入れた感じです。どうなんだろ・・
ともあれ、まずこのモジュールを「YAPC」ディレクトリ内のlibディレクトリに入れて、テストをtディレクトリに入れて、あらためてYAPCディレクトリから以下のようにテストを実行してみます。
$ prove -Ilib t/yapc.t -v
結果。
t/yapc.t .. ok 1 - YAPC_year ok 2 - YAPC_month ok 3 - YAPC_day ok 4 - YAPC_is_yet_ok ok 5 - YAPC_is_yet_not_ok 1..5 ok All tests successful. Files=1, Tests=5, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.03 cusr 0.01 csys = 0.06 CPU) Result: PASS
はい、ええ、まあ何とかなってるかな・・
ほんとはココで、最初にわざとテストをコケさせつつ徐々にコードの方を直していく、という経過を示せると面白いのかもしれないですが、時間もあまりないので淡白にご紹介しました。
ちなみに最終問題の最後にあった、「Time::Piece」と「Test::MockTime」もけっこう調べてみたのですが、これはちょっとわかんなかったですね・・
https://github.com/perl-entrance-org/workshop-2013-05/blob/master/slide.md#%E3%83%AF%E3%83%B3%E3%83%A9%E3%83%B3%E3%82%AF%E4%B8%8A%E3%81%AE%E5%AE%9F%E8%A3%85%E3%81%B8
でも本講の方に参加されていた id:m_shige さんのレポートを拝見したらがっつり対応されていてすごく参考になりました。よろしければぜひどうぞ。
http://m-shige1979.hatenablog.com/entry/2014/02/08/183515
ところで最後のぶつかり稽古というか、ペアプロみたいなの、すごい面白かったですね。長時間はきついかもしれないですが、適切にやれば暗黙知的な知見をうまく交換・交感できるように思いました。
そのときにちょっと相手の方とも話していたのですが、Perl入学式の資料について、講座のときは以下のスライドを見ながらやっていますけど、
http://perl-entrance.org/2013/handout/perlentrance05/index.html
テキストを効率的に読みたい&探したいようなときはこちらのマークダウンファイルの方が見やすいと思うのでお勧めします。(人によるとは思いますが)
https://github.com/perl-entrance-org/workshop-2013-05/blob/master/slide.md
以上です!!