Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari or Firefox browser.

Perl入学式

Perl入学式 #7

http://www.perl-entrance.org

  • 日時
    • 2012年7月22日(日) 14:00 - 17:00
  • 会場
    • 新大阪コワーキング

Perl入学式について

ロゴについて

Perl入学式

  • 横山陽平さん(http://yokoyamayohei.com/)にデザインしていただきました
  • この場をお借りしてお礼申し上げます

IT勉強会スタンプラリー

IT勉強会スタンプラリー

  • Perl入学式は, IT勉強会スタンプラリー(http://it-stamp.jp/)に参加しています
  • 今回のPerl入学式は, スタンプラリーの対象となる勉強会です
  • 台紙は勉強会終了後にお渡しします

喋ってる人

  • 名前 : papix (ぱぴっくす)
  • Twitter : @__papix__
  • 仕事 : 大学院生
  • 一言 : Perlって, 本当にイイですねぇ.

YAPC::Asiaに行こう!

  • Yet Another Perl Conference
    • 日本最大級のPerlの祭典!
    • 9月27日から29日まで, 東京大学で開催

Perl入学式も出ます!(予定)

本日の内容

  • 復習問題とお約束
  • mapとgrep
  • めざせ! 正規表現マスター!
  • 最終問題

復習問題

目標時間 : 20分

  • 端末から, 文字列を入力しよう.
  • その文字列にperlが含まれる場合, We love perl!と表示しよう.
  • ENDが入力されるまで, この処理を続けよう.

休憩(10分)

お約束

  1. #!/usr/bin/env perl
  2. use utf8;
  3. use strict;
  4. use warnings;
  5. binmode STDIN, ":encoding(UTF-8)";
  6. binmode STDOUT, ":utf8";
  7. binmode STDERR, ":utf8";
  • スクリプトを書く前に, このお約束のコードを書くようにしましょう.
  • これ以下, お約束は省略します.

mapとgrep

  • mapとgrepって何?
    • 「配列を操作する, 結構スマートなやり方」です!

実際にmapを使ってみよう!

  • 例えば, ある配列の全ての要素に含まれる@atに書き換えたい場合を考えます.
    • 要はスパム対策ですね.

今までの場合...

  1. my @mails = ('papix2011@gmail.com', 'hogehoge@fugafuga.com');
  2. my @no_spam;
  3. for my $mail (@mails) {
  4. $mail =~ s/\@/ at /;
  5. push @no_spam, $mail;
  6. }
  7. print join("\n", @no_spam);
  • 今までに紹介した方法で実現しようとすると, おそらくこんな感じになると思います.

mapを使うと...

  1. my @mails = ('papix2011@gmail.com', 'hogehoge@fugafuga.com');
  2. my @no_spam = map{ my $mail = $_; $mail =~ s/\@/ at /; $mail; } @mails;
  3. print join("\n", @no_spam);
  • たった3行で実現できます! とってもシンプルでいいですね.

mapの使い方

  1. my @no_spam = map{ my $mail = $_; $mail =~ s/\@/ at /; $mail; } @mails;
  • 右側に, 入力となる配列を書きます. 今回の場合, @mailsです.
  • mapは, {}で囲った中に, 配列(今回の場合は@mails)の各要素に対して行いたい処理を書きます.
    • 入力となる配列の各要素は$_に格納されます. 但しこれは元の要素へのリファレンスなので, 元の要素を変更したくない場合は, 例のように別の変数に格納する必要があります.
  • 左側に, 結果を格納する配列を書きます. 今回の場合, @no_spamです.

実際にgrepを使ってみよう!

  • 例えば, 配列の要素から10以下の値のみを抜き出した配列を作りたい, という場合を考えます.

今までの場合...

  1. my @num = (1, 12, 10, 3, 5, 20, 8, 13, 19, 6);
  2. my @mini;
  3. for my $i (@num) {
  4. push(@mini, $i) if($i <= 10);
  5. }
  6. print join(',', @mini);
  • 今までに紹介した方法で実現しようとすると, おそらくこんな感じになると思います.

grepを使うと...

  1. my @num = (1, 12, 10, 3, 5, 20, 8, 13, 19, 6);
  2. my @mini = grep { $_ <= 10 } @num;
  3. print join(',', @mini);
  • こちらも, たった3行で実現できます! とってもシンプルでいいですね.

grepの使い方

  1. my @mini = grep { $_ <= 10 } @num;
  • 右側に, 入力となる配列を書きます. 今回の場合, @numです.
  • grepは, {}で囲った中に書かれた条件を満たす要素のみで配列を作ります.
    • この場合, 配列(今回の場合は@num)の各要素に対して10以下かどうか($_ <= 10)をチェックして, これを満たす要素のみを含む配列を作ります.
    • mapと同じく, 入力となる配列の各要素は$_で示されます.
  • 左側に, 結果を格納する配列を書きます. 今回の場合, @miniです.

map/grepを使う利点

  • 紹介したように, map/grepはfor文で書くこともできます.
  • そこであえてmap/grepを使う理由は, そのコードの意味を明確にできる, という点ではないでしょうか.
  • grepなら, 「配列にフィルターをかける」, mapなら, 「配列の要素を変換する」という意味を, コードを読む人に伝えることができます.
    • 但し, map/grepを単なるループ処理をする為に使うのはよくない, ...そうです.

練習問題(20分)

  • 配列を受け取ると, 受け取った配列の全ての要素にis perl monger!を付け足した配列を返す関数を作ってみよう.
  • 例えば, 配列の要素の1つがpapixの場合, papix is perl monger!と書き換えられます.
  • 但し, 配列の要素がyapcの場合, is perl conference!を付け足すようにしましょう.

めざせ! 正規表現マスター!

  • データ処理の強い味方, パターンマッチと正規表現について学んでいきます.
  • パターンマッチや正規表現については以前簡単に説明していますが, 今回はもっと掘り下げていきます.
  • 正規表現は非常に複雑なので, 今回だけで「正規表現マスター」になるのは難しいかもしれませんが, 「正規表現マスター」を目指してがんばっていきましょう!

パターンマッチの復習

  1. my $str = 'Perl Entrance #7 一緒にPerlを学びましょう!';
  2. print "'$str'は'Perl'を含みます." if $str =~ /Perl/;
  • 簡単なパターンマッチの例です.
  • $strの文字列中に, /で囲まれた正規表現で示される文字列が存在するなら, 「マッチした」と扱われ, 真となります.
    • よって, print文が実行されます(#6で紹介した, 後置ifを使っています).
  • この「パターン」を表現する為に, 正規表現を使います.
    • 使いこなせば, あなたのお仕事を助ける協力な武器になります.

任意の1文字

  1. my $ans = 'y';
  2. if($ans =~ /[yY]/) {
  3. print "文字列にはyないしYが含まれています.\n";
  4. }
  • []で文字をくくると, []の中の任意の1文字にマッチします.
  • よって/[yY]/は, yないしYにマッチします.

任意の1文字(否定)

  1. my $ans = 'n';
  2. if($ans =~ /[^yY]/) {
  3. print "文字列にはyないしY以外の文字が含まれています.\n";
  4. }
  • []で文字をくくり, その先頭に^を置くと, []の中にない任意の1文字にマッチします.
  • よって/[^yY]/は, yないしY以外の文字にマッチします.
  • ^は, 必ず[の後に置いて, [^の形で用います.

任意の1文字(連続)

  1. my $ans = '1';
  2. if($ans =~ /[a-c]/) {
  3. print "文字列にはa, b, cのいずれかが含まれています.\n";
  4. }
  • []の中で, 文字の間に-を挟むことによって, 文字列の範囲を表現できます.
  • この場合, [a-c][abc]と同じ意味になります. [1-5]のように, 数値に対しても利用できます.

ワイルドカード

  1. my $ans = 'get';
  2. if($ans =~ /g.t/) {
  3. print "マッチ!\n";
  4. }
  • .は, 改行文字(\n)を除く, 任意の1文字にマッチします.
  • よって/g.t/は, getgotなど, g+任意の1文字+tにマッチします.
    • .がマッチするのは1文字だけなので, goatなどはマッチしません.
    • また, gtにもマッチしません.

量指定子'?'

  1. my $ans = 'gt';
  2. if($ans =~ /g.?t/) {
  3. print "マッチ!\n";
  4. }
  • ?は, その直前の要素が0個または1個の場合にマッチします.
    • 例えばab?は, aまたはabにマッチします.
  • よって/g.?t/は, g+任意の1文字+tに加え, gtにもマッチします.

量指定子'+'

  1. my $ans = 'get';
  2. if($ans =~ /g.+t/) {
  3. print "マッチ!\n";
  4. }
  • +は, その直前の要素が1個以上の場合マッチします.
    • 例えばab+cは, abcabbbbcなどにマッチしますが, acにはマッチしません.
  • よって, /g.+tは, g+任意の1文字以上+tにマッチします.

量指定子'*'

  1. my $ans = 'great';
  2. if($ans =~ /g.*t/) {
  3. print "マッチ!\n";
  4. }
  • *は, その直前の要素が0個以上の場合マッチします.
    • 例えばab*cは, acabc, abbbbbcなどにマッチします.
  • よって/g.*t/は, gで始まりtで終わる全てのフレーズとマッチします(greatなど).

柔軟な量指定子

  1. my $str = 'Gyaaaaaaaaa!';
  2. print "マッチ!\n" if $str =~ /a{5,}/;
  3. # マッチする
  4. my $str2 = 'Gyaa!';
  5. print "マッチ!\n" if $str2 =~ /a{5,}/;
  6. # マッチしない
  • {m,n} ... その直前の要素がm回以上, n回以下繰り返す場合マッチ
  • {m,} ... その直前の要素がm回以上繰り返す場合マッチ
  • {m} ... その直前の要素がm回繰り返す場合マッチ

マッチした文字列の取得

  1. my $str = '私は perl が好きです.';
  2. if($str =~ /私は (.+) が好き/) {
  3. print "彼は, $1 が好きです.\n";
  4. # '彼は, perl が好きです'と表示.
  5. }
  • 正規表現のパターンを()を囲むと, そのパターンに一致する文字列を取得することができます.
  • 例えばこの場合, $1にはperlが入り, 彼は, perl が好きです.と表示されるはずです.

マッチした文字列の取得

  1. my $str = '私は perl と 旅行 が好きです.';
  2. if($str =~ /私は (.+) (.+) が好き/) {
  3. print "彼は, $1 と $2 が好きです.\n";
  4. # '彼は, perl と 旅行 が好きです.'と表示.
  5. }
  • 複数の()が存在する場合, 先頭から$1, $2... で取得することができます.

マッチングの原則

  1. my $str = 'Hello hoge! Hello fuga!';
  2. if($str ~= /Hello (.+)!/) {
  3. print "Nice to meet you, $1!\n";
  4. }
  • hogeを抜き出してNice to meet you, hoge!としたいので, このようなコードを書きました.
  • しかしながら, 実際にはNice to meet you, hoge! Hello fuga!と表示されます.

マッチングの原則

  1. my $str = 'Hello hoge! Hello fuga!';
  2. if($str ~= /Hello (.+?)!/) {
  3. print "Nice to meet you, $1!\n";
  4. }
  • これは, 正規表現が「なるべく長くマッチする(最長マッチ)」ようになっている為です.
  • このように, 量指定子のあとに?を付けて, 最短マッチにすれば, Nice to meet you, hoge!と出力されるはずです.

練習問題A(15分)

  1. while( chomp (my $line = <STDIN>) ) {
  2. print $line;
  3. }
  • 上記プログラムは, 標準入力で入力した文字列をそのまま出力するプログラムである.
  • これを改変して, 入力した文字列にjohnが含まれる場合のみ, 文字列を出力しないように書き換えてみよう.

休憩(10分)

置換

  1. my $str = 'abc def ghi';
  2. $str =~ s/abc/ABC/;
  3. # $str = 'ABC def ghi';
  • s/PATTERN/REPLACE/で, PATTERNREPLACEに置換します.
  • 全てのPATTERNを置換したい場合, s/PATTERN/REPLACE/gと表記します.
    • 最後にオプションとしてgを付けることで, 繰り返し評価します.

変数の使用

  1. my $str = 'perl ruby python';
  2. my $pattern = 'perl';
  3. if($str =~ /$pattern/) {
  4. print "'$str'には'$pattern'が含まれます.\n";
  5. }
  • このように, 正規表現として変数を利用することもできます.

メタ文字

  • メタ文字を使うと, 「数字とマッチ」や「アルファベットとマッチ」などといった正規表現を, より簡単に表現することができます.
  • ここでは, よく使うメタ文字を紹介します.

メタ文字(1)

  • \w ... アルファベット, 数字, アンダーバーの1文字

    • [a-zA-Z0-9_]と同じ意味です.
  • \W ... アルファベット, 数字, アンダーバー以外の1文字

    • [^a-zA-Z0-9_]と同じ意味です.
  • \d ... 数字の1文字

    • [0-9]と同じ意味です.
  • \D ... 数字以外の1文字

    • [^0-9]と同じ意味です.

メタ文字(1)

  • \s ... 空白文字にマッチ

    • [ \n\r\f\t]と同じ意味です.
  • \S ... 空白文字以外にマッチ

    • [^ \n\r\f\t]と同じ意味です.

メタ文字(1) 使い方

  1. my $str1 = '2012年7月22日';
  2. if($str1 =~ /(\d+)年(\d+)月(\d+)日/) {
  3. print "$1/$2/$3";
  4. # "2012/7/22"と表示される.
  5. }
  6. my $str2 = "この 文章 は\n 読みにく\nい で \t す\n";
  7. my $str2 =~ s/\s+//g;
  8. # $str2 = "この文章は読みにくいです";
  • \sを使えば, 余分な空白や改行を抜き取ることができます.

メタ文字(2)

  • | ... 選択一致

    • 例えば, abc|def|ghiは, abc, def, ghiのいずれかにマッチします.
  • (x) ... グループ化

    • 正規表現をグループ化します.
    • 先に説明したように, ()の中のパターンにマッチした文字列は記憶されます(後方参照).
  • (?:x) ... 後方参照しないグループ化

    • 正規表現をグループ化しますが, ()の中のパターンにマッチした文字列は記憶されません.

メタ文字(2) 使い方

  1. my $str = 'perl is good!';
  2. if($str =~ /(?:perl|ruby|python) is (good|bad)!/) {
  3. print "評価は $1 です!\n";
  4. # "評価は good です!"と表示される.
  5. }
  • perl, ruby, python|でつなぎ, ()で囲うことで, 選択一致をグループ化しています.
  • 更に, (?:とすることで, 後方参照しないようにしています.
    • その為, $1は(good|bad)のパターンにマッチした文字列となります.

正規表現のメタ文字(3)

  1. my $str = 'john is dead.';
  2. print "john\n" if $str =~ /dead./;
  3. print "john\n" if $str =~ /dead\./;
  • \ ... メタ文字を無効化する
    • 正規表現の中で特殊な意味を持つ文字(例えば/.など)を無効化します.
  • この場合. $str =~ /dead./は, john is dead!などでもマッチしてしまう(.は任意の1文字とマッチ, なので).
  • \.のようにすれば .そのものとのマッチができます.

練習問題B(10分)

  • 練習問題Aを改変して, 入力した文字列にjohnないしJohnが含まれる場合のみ, 文字列を出力しないように書き換えてみよう.

アンカー(1)

  • アンカーは, 行頭や行末など, 文字列の特定の位置とマッチします.
  • ^ ... 行頭
  • \A ... 最初の文字列
    • ^\Aはよく似ていますが, 複数の行がある文字列に正規表現を行った場合, 挙動が異なります.
    • ^は改行の直後(新しい行の行頭)にもマッチしますが, \Aは文字列そのものの先頭にしかマッチしません.

アンカー(2)

  • $ ... 行末
  • \Z ... 最後の文字列
    • $は改行の直前にもマッチしますが, \Aは文字列そのものの最後にしかマッチしません.
    • ^\Aの関係と似ています.

アンカー 使い方

  1. my $str = 'john is great';
  2. # 行頭に'john'がある場合のみマッチ
  3. print "マッチ!\n" if $str =~ /^john/;
  4. # 行末に'great'がある場合のみマッチ
  5. print "マッチ!\n" if $str =~ /great$/;

アンカー(3)

  • \b ... 文字の区切り
    • ここでの文字は, 英数字とアンダーバーを指します.
    • \bは, \w\Wの間とマッチします.
  • \B ... 文字の区切り以外

アンカー 使い方

  1. my $str = 'lyrical magical';
  2. print "マッチ!\n" if $str =~ /lyrical\b/;
  3. # マッチします.
  4. my $str2 = 'lyricalmagical';
  5. print "マッチ!\n" if $str2 =~ /lyrical\b/;
  6. # マッチしません.
  • lyricalmagicalは, lyricalの後に文字の区切りが存在しないので, 2つ目の正規表現はマッチしません.

練習問題C(10分)

  • 練習問題Bを改変して, 以下の場合のみ, 文字列を出力しないように書き換えてみよう.
    • 文字列がjohn, Johnを含む.
    • 文字列の先頭にbetty, Bettyを含む.
    • 文字列の最後にgreat, Greatを含む.

マッチングの否定

  1. my $str = 'john betty beth';
  2. print "マッチ!\n" if $str !~ /beth/;
  3. # 何も表示されない.
  • !~演算子は=~演算子の否定です.
  • よって, 正規表現とマッチした場合は偽, 正規表現とマッチしない場合は真となります.
    • この場合, 文字列にbethを含まない場合, 真となって文字列が出力されます.

区切り記号の変更(1)

  1. my $str = '/usr/local/bin/perl';
  2. print "マッチ!\n" if $str =~ m|bin/perl|;
  • 正規表現は/で区切りますが, /だと不都合な場合も多いです(例えば, URLを表記する場合など. 全ての/をエスケープする必要がある).
  • そこで, m//のように, 先頭にmを付けると, 任意の記号のペアを区切り記号として利用することができます.
  • この場合, |を区切り記号にしています. よって, /をエスケープする必要はありません.

区切り記号の変更(2)

  1. my $str = '/usr/local/bin/perl';
  2. $str =~ s|/usr/local/bin/|/usr/bin/perl|;
  • 置換の場合, このようにできます.

正規表現のオプション

  • 正規表現では, //の後にオプションを与えることができます.

繰り返してマッチ(g)

  1. my $str = 'Hello, hoge! Hello, fuga!';
  2. my @name = ($str =~ /Hello, (\w+?)!/);
  3. # @name = ('hoge', 'fuga'); となる.
  • gは, 正規表現のマッチングを繰り返し行います.
  • また, 正規表現に()が含まれる場合, マッチした文字列のうち()の中に含まれる文字列をリストとして返します.

繰り返してマッチ(g)

  1. my $str = 'Hello, hoge! Hello, fuga!';
  2. my $str =~ s/Hello/Good morning/g;
  • 置換の部分で説明したように, s///gとすると, 置換の処理を繰り返し行なってくれます.

大文字/小文字を区別しない(i)

  1. my $str = 'John and Beth';
  2. pritn "マッチ!\n" if $str =~ /john/i;
  • iは, 正規表現中のアルファベットの大文字・小文字を区別せずにマッチングを行います.
  • よって, /john/iは, johnはもちろん, JohnJOHN, jOhNなどにもマッチします.

練習問題D(10分)

  • 練習問題Cを改変して, 以下の場合のみ, 文字列を出力しないように書き換えてみよう.
    • 文字列がjohn, Johnを含む.
    • 文字列の先頭にbetty, Bettyを含む.
    • 文字列の最後にgreat, Greatを含む.
    • 文字列がtomというアルファベットを含む(大文字・小文字問わず).
  • また, 出力される文字列にpapixが含まれる場合, 全てhogeに書き換えてから出力するようにしてみよう.

質問タイム

最終問題1 (20分~)

  1. ジュース:100
  2. 弁当:300
  3. 漫画:400
  4. カップ麺:250
  5. お菓子:100
  • このようなテキストを含むprice.txtを読み込み, 左側の商品名をキーとして, 右側の価格を値を持つハッシュ%priceを作りましょう.
  • 商品名と価格の間は, :で仕切られてるとします.

最終問題2 (20分~)

  • 先程のprice.txtの価格を1.05倍(消費税を加算)したテキストを, price+.txtに書き出すようにしてみましょう.

質問タイム

お疲れさまでした

Use a spacebar or arrow keys to navigate