sironekotoroの日記

Perl で楽をしたい

Perl入学式 2019 in東京 秋開講 第4回 お疲れ様でした

Perl入学式 2019 in東京 秋開講 第4回

受講された方、サポーターの方、お疲れ様でした。 講師をやったジャージの人です。

講義に利用したスライドはMarkdown形式で公開しています。復習に使ってください。

www.perl-entrance.org

また、復習問題を用意しています。
第1回から第4回の内容で解ける問題となっています。ぜひ挑戦してみてください。
復習問題の解答例もありますので、参考にしてください。

なお、この第4回の復習問題はかなり難しく、骨のある問題となっています!(意訳:解けなくても落ち込むな)

問題の意味がわからない、とか、このような解答例はどうだろう?という方はSlackのPerl入学式チャンネル(招待フォーム)や、twitterハッシュタグ #Perl入学式 をつけて聞いてみてください。
応答速度、監視頻度などの面からSlackの方をお勧めします。

会場を提供いただいたadishさん、ありがとうございました。

www.adish.co.jp

講義の途中でちょっとだけ使ったスライド

Perlの初学者向け書籍が「リファレンス」についてどう書いているかを調べてみたものです。

docs.google.com

リファレンス!!

講義中何度も言いましたが、他言語で間接参照(C言語などのポインタ)の概念を習得している方にとっては余裕だったと思います。

しかし、間接参照の概念に初めて触れた方は、理解まで相当時間がかかって大変だと思うんですよね・・・うちがそうでした。

私自身もリファレンスについては習得まで苦労した覚えがあります。

リファレンスについては過去のエントリで大体語り尽くしてる感があるので、そちらもどうぞ。

sironekotoro.hateblo.jp

sironekotoro.hateblo.jp

Perlのリファレンスに関しては、完全な自信をもって深沢千尋さんの「すぐわかる オブジェクト指向 Perl」をお勧めします。

Perl入学式を第4回まで受講してきた方なら、今すぐにでも読み始めて大丈夫なレベル感です。

リファレンスの活用例

以下は2020年2月7日〜2020年2月14日までの日経平均のデータです。平日のみなので5日分です。

このような「日時」に紐づいたデータは色々な場で見ることができると思います。売り上げの日報や、夏休みの宿題にある毎日の気温の記録とか。

info.finance.yahoo.co.jp

日付   始値  高値  安値  終値
2020年2月14日    23,714.52   23,738.42   23,603.48   23,687.59
2020年2月13日    23,849.76   23,908.85   23,784.31   23,827.73
2020年2月12日    23,741.21   23,869.73   23,693.72   23,861.21
2020年2月10日    23,631.79   23,788.25   23,621.72   23,685.98
2020年2月7日     23,899.01   23,943.45   23,759.42   23,827.98

このデータをリファレンスなしで表現するのはとても大変です。変数がいくつも必要になることでしょう。

しかし、リファレンスを使うことで、一つの変数の中にこれらの情報をまとめることが可能になります。

まずはこのように書いてみました。求めたいのは期間中の終値(close)の最大、最小、平均です。講義中に紹介したList::Utilモジュールを使って求めています。

配列の要素にハッシュリファレンスを入れており、そのハッシュリファレンスのkeyは日付、value始値(open), 高値(high), 底値(low), 終値(close)のハッシュリファレンスです。

・・・言葉にすると???なのですが、コードを見た方がわかりやすく思えるはずです。

#!/usr/bin/env perl
use strict;
use warnings;
use List::Util qw/max min sum/;
use Data::Dumper;

my @nikkei225 = (
    {   '2020-02-14' => {
            open  => 23714.52,
            high  => 23738.42,
            low   => 23603.48,
            close => 23687.59
        }
    },
    {   '2020-02-13' => {
            open  => 23849.76,
            high  => 23908.85,
            low   => 23784.31,
            close => 23827.73
        }
    },
    {   '2020-02-12' => {
            open  => 23741.21,
            high  => 23869.73,
            low   => 23693.72,
            close => 23861.21
        }
    },
    {   '2020-02-10' => {
            open  => 23631.79,
            high  => 23788.25,
            low   => 23621.72,
            close => 23685.98
        }
    },
    {   '2020-02-07' => {
            open  => 23899.01,
            high  => 23943.45,
            low   => 23759.42,
            close => 23827.98
        }
    },

);

my @close_values;
for my $row (@nikkei225) {
    my ($date) = keys %{$row};

    # keys は配列を返すが、この例ではkeyは1つの値(日付)だけが必要なので、このようにして受け取る

    push @close_values, $row->{$date}->{close};
}

print "MAX: " . max(@close_values) . "\n";
print "MIN: " . min(@close_values) . "\n";
print "AVG: " . sum(@close_values) / scalar (@close_values) . "\n";

# scalar (@配列) で、配列の要素数を求めることができる

# MAX: 23861.21
# MIN: 23685.98
# AVG: 23778.098

欲しかった結果は得ることができました。

しかし、ここで「欲望」が囁きます。最大値、最小値を記録したのは何月何日なのだ?と。

うーん、これは難しい。そう、このリファレンスのままでは難しい

というわけで、別なデータ構造に書き直してみます。ついでに、最大と最小はList::Utilモジュールを使わずにやってみます。

配列の中にハッシュリファレンスを入れたものです。先ほどのよりもシンプルな構造です。

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

my @nikkei225 = (
    {   date  => '2020-02-14',
        open  => 23714.52,
        high  => 23738.42,
        low   => 23603.48,
        close => 23687.59
    },

    {   date  => '2020-02-13',
        open  => 23849.76,
        high  => 23908.85,
        low   => 23784.31,
        close => 23827.73
    },
    {   date  => '2020-02-12',
        open  => 23741.21,
        high  => 23869.73,
        low   => 23693.72,
        close => 23861.21
    },
    {   date  => '2020-02-10',
        open  => 23631.79,
        high  => 23788.25,
        low   => 23621.72,
        close => 23685.98
    },
    {   date  => '2020-02-07',
        open  => 23899.01,
        high  => 23943.45,
        low   => 23759.42,
        close => 23827.98
    },
);

my $max = 0;     # 最大値を記録するための変数
my $max_date;    # 最大値の日付を記録するための変数
my $min = 38915
    ; # 最少値を記録するための変数(初期値は1989年12月29日の日経平均 史上最高値)
my $min_date;    # 最少値の日付を記録するための変数
my $sum = 0;
for my $row (@nikkei225) {
    my $close = $row->{close};

# $maxよりも大きい終値($row->{close})だったら変数の値と日付を更新
    if ( $max < $close ) {
        $max      = $close;
        $max_date = $row->{date};
    }

# $minよりも小さい終値($row->{close})だったら変数の値と日付を更新
    if ( $close < $min ) {
        $min      = $close;
        $min_date = $row->{date};
    }

    # 平均を求めるために、終値の合計額を集計しておく
    $sum += $close;
}

print "MAX: $max_date : " . $max . "\n";
print "MIN: $min_date : " . $min . "\n";
print "AVG: " . $sum / scalar(@nikkei225) . "\n";

# scalar (@配列) で、配列の要素数を求めることができる

# MAX: 2020-02-12 : 23861.21
# MIN: 2020-02-10 : 23685.98
# AVG: 23778.098

これで「欲望」の求めることを達成することができました。

このように、リファレンスを習得することで、「欲しいもの」を得やすいデータ構造を作り出すことができるようになります。

・・・ところで、第2回で学んだsortを覚えていますか?配列の要素を並べ替えするための関数です。

終値を順に並べれば、もっと楽に最大値、最小値を求めることができるのでは・・・?

リファレンスのソートはなかなかに難易度が高いです。興味のある人は是非Googleで検索しつつ実装してみてください。私も書いてみました。

最大値、最小値を求めるときにsortを使ってみる(クリックで展開)

#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;

my @nikkei225 = (
    {   date  => '2020-02-14',
        open  => 23714.52,
        high  => 23738.42,
        low   => 23603.48,
        close => 23687.59
    },

    {   date  => '2020-02-13',
        open  => 23849.76,
        high  => 23908.85,
        low   => 23784.31,
        close => 23827.73
    },
    {   date  => '2020-02-12',
        open  => 23741.21,
        high  => 23869.73,
        low   => 23693.72,
        close => 23861.21
    },
    {   date  => '2020-02-10',
        open  => 23631.79,
        high  => 23788.25,
        low   => 23621.72,
        close => 23685.98
    },
    {   date  => '2020-02-07',
        open  => 23899.01,
        high  => 23943.45,
        low   => 23759.42,
        close => 23827.98
    },
);

my $sum = 0;
for my $row (@nikkei225) {
    my $close = $row->{close};

    # 平均を求めるために、終値の合計額を集計しておく
    $sum += $close;
}

my @sorted = sort { $a->{close} <=> $b->{close} } @nikkei225;

# 配列の最初の要素を求めるときの添え字は[0]
# 配列の最後の要素を求めるときの添え字は[-1]

print "MAX: $sorted[-1]->{date} : " . $sorted[-1]->{close} . "\n";
print "MIN: $sorted[0]->{date} : " . $sorted[0]->{close} . "\n";
print "AVG: " . $sum / scalar(@nikkei225) . "\n";

# scalar (@配列) で、配列の要素数を求めることができる

# MAX: 2020-02-12 : 23861.21
# MIN: 2020-02-10 : 23685.98
# AVG: 23778.098

Perl入学式 第5回 Webアプリ編

次回のPerl入学式 in東京は秋季講習の最終回、第5回 Webアプリ編です。

サブルーチンとWebアプリの基礎をやります。connpassでページを公開しています。

ただし、新型コロナウイルスの蔓延状況次第では延期の可能性があります。ご了承ください。【2月18日追記】残念ながら、第5回は延期となりました。

perl-entrance-tokyo.connpass.com

また、Webアプリを作成していくにあたり、HTMLの最低限の知識が必要となります。もし不安な方は一度下記のテキスト「HTML入学式」を見ておくことをお勧めします。

github.com