僕のYak Shavingは終わらない

車輪の再発明をやめたらそこには壮大なYakの群れが

初めてのPerl読破記録 はじめに〜第5章

※2011年9月29日追記

連休の途中までは第3版でやっていたのですが、途中で第5版に出会ってしまって最初から変更箇所を
見直しつつやってたら全然終わらなかったという。第3版の半分を2日で終わらせた矢先のことでした。。。

第3版と第5版はPerlのバージョンは違うし、章構成も変わったうえにところどころで変更と追記(だいぶ)があるので自分は最初から第5版をやることをおすすめします。

少なくとも第3版の目次と対応させてログをとっていた自分にとっては第5版にあったときの衝撃は辛かったです。

※追記終わり

この3連休の目標として以下を掲げて見ました。

  • 『初めてのPerl』を読破(金曜日の時点で1/4終了)
  • 『続・初めてのPerl』を読破
  • 今作っているAndroidアプリの完成
  • Herokuを使ってFacebookアプリを作る
  • Androidアプリ用に作ったAPIをdotcloudに移行

さーてどこまでできるでしょうかw

ちなみにここに書いてあるコードなんかは、本を見て全部を自分流に書きなおしたものです。
本の意図していないコードでも自分的に動作の気になるものは書いて実行して確認してみます。
ただ勉強用のメモ程度にしか説明してないので、このページでPerlの勉強をすることはまったくおすすめしません。
自分で本を買って読むのが一番いいですよ!

初めてのPerl 第5版

初めてのPerl 第5版


続・初めてのPerl 改訂版

続・初めてのPerl 改訂版

ということで早速行ってみます!


・・・と思って始めようとしていたらCTOがふらふらやってきて

「はじパ読むの?意味無いでしょー?プログラミングPerl読みなよー」

と言って去って行きました…

が、しかし『はじパ』からやりますw

一度ざっと目を通す

表紙、目次、中身の印象、ネットでの評価を一度ざっくり見ます。

で、最初に遭遇した問題は本の型番が古いということ。
自分が持っているのは第3版でした。
今は第5版が最新なんですねー

どうしようと思ったけど、とりあえず進めることにします。
どの道第5版だろうと最新じゃないんだし。
(あとで後悔するのは、kazuph!お前だからな!)

ちなみに第3版の正誤表とサンプルコードはこちら。
http://www.oreilly.co.jp/books/4873111269/

刷数がある程度行っていればすでに修正済みのようです。

サンプルダウンロード

wget http://examples.oreilly.com/9780596001322/llama-sample-files-1.1.tgz
tar zxvf llama-sample-files-1.1.tgz
tree sample_files
sample_files/
├── README
├── fake_date
├── lnr_example
├── numbers
├── pattern_test
├── sortable_hash
├── text_files
│   ├── bamm-bamm
│   ├── barney
│   ├── betty
│   ├── dino
│   ├── fred
│   ├── pebbles
│   ├── sample_text
│   └── wilma
└── which_dbm

はじめに

以下適当なつぶやき。


「もしあなたがPerlプログラミン言語を学ぶために最初の30〜40時間を費す最良の方策を探しているなら〜」
お、つまりこの連休中に終わるってことですね!←

本を書くとムショに行かなくても良いことになるらしい。

脚注の内容がマニアック過ぎる!
しかも「脚注の多くは、単なるコンピュータージョークに過ぎません」
自覚してやってるwww

Perlはラクダにちょっと似ています。」
確かに。

第1章 Perl入門

いつの間にか読み終わってた。
練習問題に行く前に練習問題も解き終わってた←

第2章 スカラーデータ

ここからコードを書いて来ますが、そのほとんどが本に載っているコードとは違うと思います。
本と照らしあわせながらこんな書き方もするんだぁ、とかmyをつける場合はこうやるんだとか(本の最初はmyは使ってないので)とかを実行できる環境で確認しながらやると勉強になるかもしれないしならないかもしれないし…。

2.3 文字列

↓なにこれ知らなかったw

print 5 x 4 #5555と出力
  • 2.3.4 数値と文字列間の自動変換

↓ぎゃぁー!

print "5fred23425" * "          3" #色々無視して15と出力
2.8 chomp演算子

chompとか出会わない。。。

2.9 while制御構造

(なんかwhile分の扱いがひどかったなぁ、まあその程度のものなんだろうけど)

2.10 未定義値
2.11 defined関数

未定義の変数は素通りできても警告は履くよね。

2.12 練習問題

↓とりあえず練習問題の一部。

use strict;
use warnings; 

chomp(my $moji = <STDIN>);
chomp(my $cnt = <STDIN>);
print  "$moji\n" x $cnt;

(ちなみにこれより下のソースではuseはめんどくさいので書かれてませんが、ちゃんと使っているので悪しからず。myとかも本には書いてませんがここでは適宜つけていきます。)

ふう、次!

第3章 リストと配列

3.1 配列の要素にアクセスする

うっ…

my @fred;
$fred [0]=1;
print $fred[0.248572309],"\n"; #添字は小数点以下切捨てられて出力は1
3.2 配列の特別なインデクス

$#ってなんかおえ〜

my @fred;
$fred [0]='start';
$fred [99]='end';
print $fred[0],"\n";
print $fred[50],"\n"; #fred[1~98]はundefなので怒られる
print $fred[99],"\n";

#oeeee
$#fred  = 0 ;
print $fred[0],"\n";
print $fred[99],"\n"; #怒られる
print $#fred,"\n";

なるほど-1って使えたんだ。

$#fred  = 99 ;
$fred[$#fred]='The End';
print $fred[-1],"\n"; #The End が表示される
3.3 リストリテラル
  • 3.3.1 qwショートカット

リストの書き方って便利だよね。
(出力結果は随時実行して確かめて見てください。)

my @num = 1..10;
my @fred = qw/hoge fuga piyo /;
my @mark = qw{/usr/bin/perl /etc/hosts };

print $num[0],"\n";
print $num[9],"\n";
print $fred[0],"\n";
print $fred[1],"\n";
print $fred[2],"\n";
print $mark[0],"\n";
print $mark[1],"\n";

..をたくさん使いたいときは()で囲う必要があるみたい

my @num_ = (0, 2..6, 20..25);
my $i=0;
while($i <= $#num_){
    print $num_[$i],' ';
    $i+=1;
}
print "\n";
3.4 リスト代入

リスト代入

(my $hoge, my $fuga, my $piyo) = ("hoge", "fugafuga", "piyopiyopiyo");
print $hoge,"\n";
print $fuga,"\n";
print $piyo,"\n";

こうも書けるよね

my ($hoge, $fuga, $piyo) = ("hoge", "fugafuga", "piyopiyopiyo");
print $hoge,"\n";
print $fuga,"\n";
print $piyo,"\n";

溢れたら無視するんだって

my ($hoge, $fuga, $piyo) = 1..10; #最初の3つだけ代入されてあとは無視される
print $hoge,"\n";
print $fuga,"\n";
print $piyo,"\n";

変数の方が多いとundefが入るだけ

my ($hoge, $fuga, $piyo) = qw/hoge fuga/; #最初の3つだけ代入されてあとは無視される

print $hoge,"\n";
print $fuga,"\n";
print $piyo,"\n"; #怒られる

こういうのってよく関数作るときに利用されますよね。

ていうかスルーしてたけど値の交換ってこれだけで出来るんだ!

my ($hoge, $fuga) = qw/hoge fuga/;

print $hoge,"\n";
print $fuga,"\n";

($hoge, $fuga) = ($fuga, $hoge);

print $hoge,"\n";
print $fuga,"\n";

あれ?Perlのバージョンのせいか本とは違う結果になる?(ちなみに自分は5.12.4使用、本は5.8?)

my @array0 = qw/hoge undef fuga/; #この場合はundefはただの文字
print $array0[0], "\n";
print $array0[1], "\n";
print $array0[2], "\n\n";

my @array1 = ("hoge", undef, "fuga"); #この場合はundefはただの文字
print $array1[0], "\n";
print $array1[1], "\n"; #意図的にundefを挿入している→怒られる
print $array1[2], "\n\n";

my $tmp = ();
my @array2 = ("hoge", $tmp, "fuga"); #この場合はundefはただの文字
print $array2[0], "\n";
print $array2[1], "\n"; #本の通りならundefは入っていないはずだがundefが入ってしまっている
print $array2[2], "\n";
print @array2.length, "\n"; #配列の長さも3となる

$tmp=();としても空が入っているわけでなく普通にundefとして定義されているみたい。
別にそういう事ならそういうことでいいのだけれどね。
(…やっぱ第5版にすればよかったかなぁ…)

ぽっぷ
後ろから順々に出してく

my @array = (5..9);
print @array."\n";
print "\n";

while(@array.length >0){
    print pop(@array)."\n";
    print @array."\n";
    print "\n";
}

ちょっとここでは関係ないけど知らなかったのは以下。
,で区切るとリストで表示はされるのは普通か。
.で区切ると@arrayが配列の大きさを表示する方の変数として解釈されている。
ふーん、ですね。

my @array = (5..9);

while(@array.length >0){
    print pop(@array),"\n";
    print "配列の中身を表示:",@array,"\n"; #,で区切る
    print "配列の長さを表示:".@array."\n"; #.で区切る
    print "\n";
}

ぷっしゅ
末尾に追加していく

my @array = (5..9);
my @pushed = ();

while(@array.length >0){
    push(@pushed, pop(@array));
    print @pushed,"\n";
    print @pushed."\n";
    print "\n";
}

あとしふととあんしふとは先頭からちょめちょめするらしいよ←適当!(俺が)

my @array = (5..9);
my @unshift;

while(@array.length >0){
    unshift(@unshift, shift(@array)),"\n";
    print @unshift,"\n";
    print "配列の中身を表示:",@unshift, ,"\n"; #,で区切る
    print "配列の長さを表示:".@unshift."\n"; #.で区切る
    print "\n";
}
3.5 配列を文字列の中に展開する

配列の展開のされ方って色々あるんだね〜

my @rock = qw/stone slate rubble/;
print @rock,"\n";
print @rock."\n";
print "@rock\n";
print "hoge @rock fuga\n";
print "hoge",@rock,"fuga\n";
print "hoge".@rock."fuga\n";

おっと、ここでも"2*4"は数字の2って解釈されるんだね。

my @rock = qw/stone slate rubble/;
my $x = 2;
print "hoge $rock[$x-1] fuga\n";

my $y = "2";
print "hoge $rock[$y-1] fuga\n";

my $z = "2*4";
print "hoge $rock[$z-1] fuga\n";
#すべて結果は同じになる

えー!!配列とスカラーで同じ文字でも違うものとして使えたんだ!
知らなかった…。

my @fred = qw{eating rocks is wrong};
my $fred = "right";

print "$fred[0]\n";
print "$fred[1]\n";
print "$fred[2]\n";
print "$fred[3]\n";
print "$fred\n";

なので表示するときんは気をつけて書かないと予期せぬ結果になる。

my @fred = qw{eating rocks is wrong};
my $fred = "right";

print "This is $fred[3]\n"; #wrong
print "This is ${fred}[3]\n"; #right
print "This is {$fred}[3]\n"; #{right}
print "This is $fred"."[3]\n"; #right
print "This is $fred\[3]\n"; #right
3.6 foreach制御構造

来ましたよforeach

foreach my $array (qw/hoge fuga piyo/){
    print "$array\n";
}

知らなかった!!
単なる@arrayの"値"を$valueに代入しているつもりしかなかったけど、
$value自体が@arrayの値そのものなんだ!
とにかく以下を実行。

my @array = qw/hoge fuga piyo/;
print @array, "\n"; #スペース無しでくっついて表示される
foreach my $value (@array){
    $value = "$value ";
}
print @array, "\n"; #スペースありで表示される
3.7 Perlお気に入りのデフォルト:$_

ちなみにこの場合の$value(制御変数と呼ばれる)は以下の用に$_で代用できる。

my @array = qw/hoge fuga piyo/;
print @array, "\n"; #スペース無しでくっついて表示される
foreach (@array){
    $_ = "$_ ";
}
print @array, "\n"; #スペースありで表示される

ちなみにprintって基本的に指定がない場合は$_を表示するだとか。

my @array = qw/hoge fuga piyo/;
print @array, "\n"; #スペース無しでくっついて表示される
foreach (@array){
    $_ = "$_ ";
    print;
    print "\n";
}
print @array, "\n"; #スペースありで表示される

あと反転の使い方

print 1..10, "\n";
print reverse(1..10)."\n";
print reverse 1..10 ,"\n"; #ちなみにこれだと変な結果になりますね。まあそうですね?

print "\n\n";

#あと変数を使った時
my @hoge = 3..9;
my @fuga = reverse @hoge;
print @hoge,"\n"; # 元のまま
print @fuga,"\n"; # もちろん反転している

そーと

print 1..9, "\n"; #123456789
print reverse(1..9), "\n"; #987654321
print sort(reverse(1..9)), "\n"; #12345689
3.8 スカラーコンテキストとリストコンテキスト

スカラーコンテキスト、リストコンテキストの話になりました。
この話はさっきしたprintで,と.で表示される内容が違う、っていうことには関係してそうです。

my @list = 1..10;
my $n = @list;
print @list,"\n"; #12345678910
print @list."\n"; #10
print $n."\n"; #10
  • 3.8.1 リストを生成する式をスカラーコンテキストで使う

reverseの場合

my @moji = qw/hoge fuga piyo/;
my @moji_ = reverse @moji; #リストとして代入される
my $moji = reverse @moji; #
print @moji,"\n"; #元の配列
print @moji_,"\n"; #要素の順番が反転
print $moji,"\n"; #すべての要素をくっつけて一文字にしてそれを反転
print @list,"\n"; #12345678910
print scalar @list,"\n"; #10
print @list."\n"; #10
3.9 リストコンテキストでを使う

上の方でやった練習問題に対して適用。

print "最初に文字を入力してEnter、次に回数を入力してEnter、ちなみにそれ以降の何を入力しても無視され>ます。\n";
print "あとCtrl+Dで入力終了\n";
chomp(my @moji = <STDIN>);
print  "$moji[0]\n" x $moji[1];
3.10 練習問題

はい!っていうことで練習問題3つくらいあったけど全部含ませてみた。
(↓超適当だけど気にしない)

print "基本的に入力終了は自分で決めて。入力終了はCtrl+Dだよ\n";
print "人の名前を適当に何人か入力してちょ\n";
chomp(my @hito = <STDIN>);
print "あんたが入力したのはこの人達だね\n";
foreach (@hito){
    print $_,"\n";
}
print "逆順に出力しても全然面白くないよね。。。\n";
foreach (reverse @hito){
    print $_,"\n";
}
print "ソートしてみたけどおもしろい、これ?\n";
foreach (sort @hito){
    print $_,"\n";
}

print "やめたくなったらCtrl+Cで強制終了してくれ\n";
while (1){
    print "0~".(scalar @hito -1)."までの数字を入力してみて!\n";
    chomp(my $num = <STDIN>);
    print "その番号に対応する人は、".$hito[$num]."だよ!\n";
}

ということで次行くぜ!!

第4章 サブルーチン

4.2 サブルーチンを定義する

サブ!マリン!バカ!

4.4 戻り値

Perlのサブルーチンも最後に計算・評価した結果を戻り値に出来るんだね。

sub marine {
    $_[0] + $_[1];
}
print &marine(1,2),"\n"; #3を出力

間違って最後にprintにするとprintが成功したという意味で1を返しちゃう。

sub marine {
    $_[0] + $_[1];
    print "hoge";
}
print &marine(1,2),"\n"; #hoge1と表示されてしまう
4.5 引数

↓は動作するけどかっこが悪いらしい。

sub max {
    if ($_[0] > $_[1]){
        $_[0];
    }else{
        $_[1];
    }
}
print &max(4,2),"\n";
4.6 サブルーチンの中でプライベートな変数

以下みたいにサブルーチン内だけで使用できるレキシカル変数を定義できるよ。

sub max {
    my($a, $b) = @_;
    if ($a > $b){
        $a;
    }else{
        $b;
    }
}
print &max(4,2),"\n";

(好みじゃない?)

4.7 local演算子
  • 4.7.1 localとmyの違い

localとmyの違いがやっとわかったぜ!

4.11 return演算子
my @names = qw/take yuji keiko soya kota akira taro/;
chomp(my $name = <STDIN>);
if(&iru($name,@names)){
    print "iruyo\n";
}else{
    print "inaiyo\n";
}

sub iru{
    my($name, @list) = @_;
    foreach(@list){
        if($name eq $_){
            return 1;
        }
    }
    undef;
}
4.12 練習問題

つまらないのでやらなかった。

ふぅ、次!

第5章 ハッシュ

5.2 ハッシュの要素にアクセスする

[]をブラケット、{}をブレースと呼ぶ。
テストにはでない。

  • 5.2.1 ハッシュ全体を扱う

ここまで読んでみて、すぐ3.10の練習問題をハッシュを使えば短く出来るんじゃないかって思ってやってみる。
本はちゃんと読んでないけど。

my %names = ( "take" => 1, "yuji" => 1, "keiko" => 1, "soya" => 1, "kota" => 1, "akira" => 1, "taro" => 1);
#このハッシュいるの?wっていう冷徹なツッコミは断固として拒否する!
#いいじゃない、短くなったんだから。

chomp(my $name = <STDIN>);
if($names{$name}){
    print "iruyo\n";
}else{
    print "inaiyo\n";
}

それにしてもPerlってすごいよねー、
これだけやっても下の変数名が”重複してない”ことになるんだから。

#全部aでも別物として認識する
my $a = 1;
my @a = 1..5;
my %a=(
    "hoge" => "hogeko",
    "fuga" => "fugao",
);
sub a{
    1234;
}
print $a,"\n";
print $a[0],"\n";
print $a[3],"\n";
print $a{hoge},"\n";
print $a{fuga},"\n";
print &a,"\n";


ハッシュってコンテキストによってどうなるのかを調べてみる。

# なんだこんな定義でも勝手にペアにしてくれるのか…
my %hash_list=(
    "hoge", "hogeko", "fuga", "fugao",
);
print $hash_list{hoge},"\n";

my $n = %hash_list; # わからんw 読み進めたら「Perlをメンテナンスする人の内部デバッグ情報を表す文字列」を返すらしい
my @list = %hash_list; # 単純に上で定義してるようなリストになる
print $n,"\n";
print @list,"\n";
5.3 ハッシュ関数
  • 5.3.1 keys関数とvalues関数
  • 5.3.2 each関数
# 可読性を考慮して太矢印で書きましょう
my %hash_list=(
    "hoge" => "hogeko",
    "fuga" => "fugao",
    "yamada" => "taro",
    "satou" => "keiko",
);

#keysとvaluesの使い方
#リストコンテキスト
my @keys = keys %hash_list;
my @values = values %hash_list;
print @keys, "\n";
print @values, "\n";

#keysとvaluesの使い方
#スカラーコンテキスト
my $keys_num = keys %hash_list;
my $values_num = values %hash_list;
print $keys_num, "\n";
print $values_num, "\n";

#eachを使ったループ文
while(my($key, $value) = each %hash_list){
    print "$key => $value\n";
}

あくまでeachは%hashのkey/valueを順番に出して行っているだけ。

そしてwhile文も出されたリストのをスカラーコンテキストとして読み取ってリストの個数が非ゼロかどうかを判定しているだけでeachから何か特別な値をもらっているわけではないと解釈することが重要。

ともするとハッシュをループしたいときはこういう定型文を使うって覚えてしまいがちだけど、
ちゃんと理解して使わないと本質を見失うなぁ。

はじパはいい本ですね。

keyの順番を辞書順で出力したいとき

my %hash_list=(
    "hoge" => "hogeko",
    "fuga" => "fugao",
    "yamada" => "taro",
    "satou" => "keiko",
);

# 順番ごちゃ混ぜ
foreach (keys %hash_list){
    print $hash_list{$_}, "\n";
}

# 辞書順で出力
foreach (sort keys %hash_list){
    print $hash_list{$_}, "\n";
}
5.4 ハッシュの利用法
  • 5.4.1 exists関数
  • 5.4.2 delete関数
# 可読性を考慮して太矢印で書きましょう
my %hash = (
    "hoge" => "hogeko",
    "fuga" => "fugao",
    "yamada" => "taro",
    "satou" => "keiko",
);
print "消したい人の名前(姓)を入力てね\n";
chomp(my $name = <STDIN>);
if(exists $hash{$name}){
    delete $hash{$name};
    print "消しておいたよ!\n";
}else{
    print "そんな人いないよ!\n";
}
print "Name List\n";
foreach (keys %hash) {
    print "$_ $hash{$_}\n";
}
  • 5.4.3 ハッシュの要素を変数展開する

%hashなんて書いても意味なし。

my %hash = (
    "hoge" => "hogeko",
    "fuga" => "fugao",
    "yamada" => "taro",
    "satou" => "keiko",
);
print "Name List $hash{hoge}\n";
print "Name List %hash\n";
5.5 練習問題

例によって勝手に色々自分で仕様を色々追加する。

# 空のハッシュを用意
my %name_hash;
# 複数行名前を入力してもらう
print "適当に名前を入力。あえて重複して入力して下さい。\n";
print "Ctrl+Dで終了です。\n";
chomp(my @name_list = <STDIN>);
foreach (@name_list){
    if(exists $name_hash{$_}){
        $name_hash{$_} +=1;
    }else{
        $name_hash{$_} = 1;
    }
}

print "名前とその入力回数\n";
foreach (sort keys %name_hash){
    print "$_ : $name_hash{$_} 回\n";
}

よっしゃー5章終わったー長かった!
続きはまた明日。


続く・・・