「初めてのPerl」第6版を購入。

所用にてスーパーPerlプログラマーにならざるを得ない感じなので、初めてのPerlを購入しました。Perlさんマジ無理っす。

リャマ本だけではスーパープログラマになることはできないのですね…。モジュールの作り方とかOOPとかは続編のアルパカ本で詳しく解説しているのだとか。しかし改訂第2版が先月出たばかりという、えっ、これ買えっていう神の思し召し!?

練習問題の解答

とりあえずこの記事では、モチベーションの維持のために、練習問題を解いた結果を順次載せていきたいと思います。コードが合ってるかどうかは付録見るのも面倒なので確認していません。

1章 Perl入門

写経して実行するだけなので省略。

2章 スカラーデータ

"$calar"です。Perlは文字列と数値の区別が殆ど無い…恐ろしい子。

myは、まだ説明がないのでよく分かりませんが*1、use strictすると徹底的に文句言われるのでつけています。

πはMath::Trigとかいうモジュールをほにゃららすると取得できるようですが、モジュール関係の話はまだ出てきてないのでハードコーディングしてます。

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

my $problem = $ARGV[0];
my $PI = 3.141592654;

if ($problem == 1) {
    say 12.5 * 2 * $PI;
}

if ($problem == 2) {
    my $line = <STDIN>;
    chomp($line);
    say $line * 2 * $PI;
}

if ($problem == 3) {
    my $line = <STDIN>;
    chomp($line);
    if ($line < 0) {
        say 0;
    } else {
        say $line * 2 * $PI;
    }
}

if ($problem == 4) {
    my $line1 = <STDIN>;
    chomp($line1);
    my $line2 = <STDIN>;
    chomp($line2);
    say $line1 * $line2;
}

if ($problem == 5) {
    my $line1 = <STDIN>;
    my $line2 = <STDIN>;
    chomp($line2);
    print $line1 x $line2;
}

3章 リストと配列

"@rray"です。コンテキストという概念を知ったことで、少しPerlの理解が深まった気がします。

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

my $problem = $ARGV[0];

if ($problem == 1) {
    my @lines = <STDIN>;
    print reverse @lines;
}

if ($problem == 2) {
    my @names = qw{fred betty barney dino wilma pebbles bamm-bamm};
    chomp(my @lines = <STDIN>);
    foreach (@lines) { say $names[$_-1]; }
}

if ($problem == 3) {
    chomp(my @lines = <STDIN>);
    my @sorted_lines = sort @lines;
    say @sorted_lines;
    foreach (@sorted_lines) { say; }
}

4章 サブルーチン

Perlではユーザが書いた関数を「サブルーチン」と呼ぶようです。「関数」と呼ぶと、組み込み関数の意味も含まれるのだとか。

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

my $problem = shift;

sub total {
    my $sum = 0;
    foreach (@_) { $sum += $_; }
    $sum;
}

if ($problem == 1) {
    my @fred = qw{ 1 3 5 7 9 };
    my $fred_total = total(@fred);
    print "The total of \@fred is $fred_total.\n";
    print "Enter some numbers on separate lines: ";
    my $user_total = total(<STDIN>);
    print "The total of those numbers is $user_total.\n";
}

if ($problem == 2) {
    say total 1..1000;
}

sub average {
    total(@_) / @_;
}

sub above_average {
    my $a = average @_;
    my @r;
    foreach (@_) {
        push @r, $_ if $_ > $a
    }
    @r
}

if ($problem == 3) {
    my @fred = above_average(1..10);
    print "\@fred is @fred\n";
    print "(Should be 6 7 8 9 10)\n";
    my @barney = above_average(100, 1..10);
    print "\@barney is @barney\n";
    print "(Should be just 100)\n";
}

sub greet {
    state $name1;
    if (!$name1) {
        $name1 = shift;
        say "Hi $name1! You are the first one here!";
    } else {
        my $name2 = shift;
        say "Hi $name2! $name1 is also here!";
    }
}

if ($problem == 4) {
    greet "Fred";
    greet "Barney";
}

sub greet2 {
    state @names;
    my $name = shift;
    if (@names == 0) {
        say "Hi $name! You are the first one here!";
    } else {
        print "Hi $name! I've seen:";
        foreach (@names) { print " $_"; }
        print "\n";
    }
    push @names, $name;
}

if ($problem == 5) {
    greet2 $_ for qw/Fred Barney Wilma Betty/;
}

折角なので後置のifやforを使ってみました。既に初見では理解不可能なコードになっている気がしますが…。

グローバルスコープだとshiftの引数に@ARGVが、サブルーチン内では@_がデフォルトで入るようですが、どういう仕組でこうなっているのか未だ謎です*2

5章 入出力

この章は覚える事が多いです。とりまさらっと流して、必要に応じて参照し直すのが良さげです。

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

$problem = shift;

if ($problem == 1) {
    print reverse <>;
}

if ($problem == 2) {
    say((1..9, 0) x 3);
    printf "%20s\n", $_ foreach (@ARGV);
}

if ($problem == 3) {
    my $width = shift;
    say((1..9, 0) x ($width / 10 + 1));
    printf "%${width}s\n", $_ foreach (@ARGV);
}

あれ、各解答が3行以内に収まってますね(汗)。ついつい圧縮してしまうPerl怖っ。

6章 ハッシュ

Pythonで言うところの辞書型ですね。昔はPerlでは連想配列って読ばれてた気が…って思ったら、英語で書く時長いので(associative array)ハッシュに統一されたそうな。

それと、この章で重要なのは、Perlでは'=>'をファットカンマ*3と呼ぶようで、プログラムのどこにおいても普通のカンマ','と同じように扱われる、というお話です。これを知ったお陰で少し本格的なコードが読めるようになった気がします。

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

my $problem = shift;

if ($problem == 1) {
    # コンピュータにハマりすぎて知り合いがいないので
    my %DATA = (
        fred => 'flintstone',
        barney => 'rubble',
        wilma => 'flintstone',
    );
    my $first_name = shift;
    if ($first_name and exists $DATA{$first_name}) {
        say $DATA{$first_name};
    } else {
        say "not exists."
    }
}

if ($problem == 2) {
    my %words;
    while (<>) {
        chomp;
        $words{$_} += 1
    }
    foreach (sort keys %words) {
        say "$_: $words{$_}";
    }
}

if ($problem == 3) {
    my $max_key_length = 0;
    foreach (keys %ENV) {
        if (length($_) > $max_key_length) {
            $max_key_length = length($_);
        }
    }
    foreach (sort keys %ENV) {
        printf "%${max_key_length}s : $ENV{$_}\n", $_
    }
}

少々しんどくなってきました。

7章 正規表現の世界

まぁ正規表現なら楽勝でしょーって思っていたら、キャプチャグループという初耳の機能がありました。でもこれ意外と便利かも。

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

my $problem = shift;

if ($problem == 1) {
    while (<>) {
        if (/fred/) {
            print $_;
        }
    }
}

if ($problem == 2) {
    while (<>) {
        if (/[Ff]red/) {
            print $_;
        }
    }
}

if ($problem == 3) {
    while (<>) {
        if (/[.]/) {
            print $_;
        }
    }
}

if ($problem == 4) {
    while (<>) {
        if (/[A-Z][a-z]+/) {
            print $_;
        }
    }
}

if ($problem == 5) {
    while (<>) {
        if (/(\w)\1/) {
            print $_;
        }
    }
}

if ($problem == 6) {
    while (<>) {
        if (/(wilma.*fred)|(fred.*wilma)/) {
            print $_;
        }
    }
}

9章まで正規表現、10章がmore制御構造、11章でようやくモジュールのようですね。先長い、けど時間もない。やるとこはやって適宜読み飛ばすことにします。

*1:おそらくローカル変数宣言だと思いますが、関数スコープなのか括弧スコープなのか。まぁこの後説明されるでしょう。

*2:この辺りの説明もあとの方で出てくるのだろうか

*3:他の言語ではファットアローとか呼ばれたりしますが。