sironekotoroの日記

Perl で楽をしたい

Perl を AWS Lambda で使って Hello World! する

前回までのあらすじ

AWS 何もわからん・・・でもやらないと・・・そんな気持ちだけが先走った状態をやっと抜け、 AWS のハンズオンに取り組み始めた sironekotoro 。

しかし、ここで自分が動画ハンズオンが苦手なことに気づいてしまう。追い討ちをかけるように低速度回線(WiMAX 夜間108kbps)が進捗と精神を蝕む。

そして、どうしても AWS Lambda で Perl を動かしたい!という気持ちが抑えられないのだった。

前提

AWS の勉強はこれをやってます。

qiita.com

書いてある順にこなし、AWS Lambda のところまで来ました。

pages.awscloud.com

AWS公式で掲載されているハンズオンも 2019 年に収録されているものなので、AWS のコントロールパネルの配置や情報のたどり方が違っています。

進化の早い業界なので仕方ないですね・・・でも、大体なんとかわかります。

あ、一点だけ、途中に出てくる JSON が invalid なのでそこだけは注意かなぁ。

invalid な(よろしくない)やつ

{
    "statusCode": 200,
    "body": {
        {
            "report_id": 5,
            "report_title" : "Hello, world"
        },
        {
            "report_id": 7,
            "report_title" : "Good morning!"
        }
    }
}

多分こうじゃないかなという

{
    "statusCode": 200,
    "body": [
        {
            "report_id": 5,
            "report_title" : "Hello, world"
        },
        {
            "report_id": 7,
            "report_title" : "Good morning!"
        }
    ]
}

ハンズオン自体はまず Lambda で関数を作り、API GatewayAPI 作って GET を叩き、最後に DynamoDB で入出力の記録を収録するというものです。

途中で書くコードは AWS Lambda が標準でサポートしている Python でした。

Perl 書いて Lambda で Hello,World!

準備

AWS Lambda で Perl を動かす方法はいろいろあるみたいなのですが、今回はレイヤーを使いました。

docs.aws.amazon.com

レイヤーは、ライブラリ、カスタムランタイム、またはその他の依存関係を含む .zip ファイルアーカイブです。レイヤーを使用することで、関数のライブラリを使用することができます。

「Lambda Perl」で検索すると、このカスタムランタイムなるものを使えば Perl が動く、ってことが書いてある・・・あるけど、具体的にどうやるのか分からず。

が、分からないまま進めていきます。蛮勇。

github.com

Perl のレイヤー一覧見ていくと、AWS のリージョンごとに提供されている模様。

今回、自分は東京リージョンで Lambda を作成したので arn:aws:lambda:ap-northeast-1:445285296882:layer:perl-5-32-runtime-al2:3 を用います。

具体的な進め方

結局、試行錯誤したんで、かつての自分が知りたかったことを書いていきます。いつか、「Perl 入学式 in AWS」 でやるかもしれない・・・(そんな企画はない

  1. AWS の Lambda のページを開く

  2. 「関数の作成」のページ

    • 画面上部にあるオプションは「一から作成」を選択
    • 関数名は任意ですが、今回は「perl_test」とします
    • ランタイムはプルダウンメニュー最下部にある「カスタムランタイム」の中の「Amazon Linux 2 でユーザー独自のブートストラップを提供する」を選択
      • shogo82148 / p5-aws-lambdaUse Prebuild Public Lambda Layers3. For the "Runtime" selection, select Provide your own bootstrap on Amazon Linux 2. より
    • 画面右下のオレンジ色の「関数の作成」をクリック f:id:sironekotoro:20210312223315p:plain
  3. 「関数の概要」部の「Perl_test」の下にある「Layers」をクリック f:id:sironekotoro:20210312223805p:plain

  4. 遷移した先の「レイヤーの追加」ボタンをクリック f:id:sironekotoro:20210312224009p:plain

  5. レイヤーの情報を入力する

    • 「ARN を指定」のラジオボタンを選択
    • 「ARN を指定」のテキストボックスに arn:aws:lambda:ap-northeast-1:445285296882:layer:perl-5-32-runtime-al2:3 をコピペ f:id:sironekotoro:20210312224422p:plain
    • 画面右下のオレンジ色の「追加」をクリック
  6. 「関数の概要」部の「Perl_test」の下にある「Layers」の右に (1) となっていればレイヤーの追加に成功している

    f:id:sironekotoro:20210312231120p:plain

  7. 「関数の概要」の下にあるコードソースのところで新しいファイルを作る

    • File から New File を選択

      f:id:sironekotoro:20210312231215p:plain

    • 右側のテキストフィールドに以下のコードを記述またはコピペ

      • このサブルーチン名 hundler は固定

    gist.github.com

    • コードを書いたら File から Save を選択し hello.pl という名前で保存する
      • このファイル名 hello.pl は今のところ固定

    f:id:sironekotoro:20210312233303p:plain

    f:id:sironekotoro:20210312233526p:plain

    • 左側のファイル一覧に保存した hello.pl が増えてることを確認する

    f:id:sironekotoro:20210312233751p:plain

  8. デプロイする

    • コードの編集・保存をする都度、上にあるオレンジ色の「Deploy」ボタンを押す

    f:id:sironekotoro:20210312235132p:plain

    • コードの保存をしても、この「Deploy」ボタンを押さないと Lambda 上では変更が反映されないので注意

    • 「Deploy」ボタンを押すと、ボタン右の表示が緑色になり「Deploy」ボタンが押せなくなる

    f:id:sironekotoro:20210312235434p:plain

  9. コード入力欄の上にある「Test」をクリックする

    f:id:sironekotoro:20210312234648p:plain

    • ラジオボタンは「新しいテストイベントの作成」をクリックする
    • イベントテンプレートは hello-world のまま
    • イベント名は適当に PerlTest とかで
    • 入力が終わったら、画面右下のオレンジ色の「作成」ボタンを押す

    f:id:sironekotoro:20210312234319p:plain

  10. 再度「Test」ボタンを押す

    • コードを書いたテキストフィールドの上に「Execution results」タブが増えており、そこにテスト結果が入っている

    f:id:sironekotoro:20210313001802p:plain

もう一歩

ここまでだと、単にテストケース増やしただけなので、もう少しコードを増やしてみます。

増やしたら保存&「Deploy」を忘れずに。

gist.github.com

f:id:sironekotoro:20210313001815p:plain

ということで

Lambda でも Perl 使えるよ!ってな記事でした。

公式の AWS Lambda ハンズオンのように、他のサービスに繋げるのもできるのではないかなぁと思います(やってない)。

ただ、現状では標準モジュール以外のモジュール(cpanm とかでインストールするもの)が使えません。

どうも、モジュールを zip で固めてレイヤーで追加する・・・らしいんですが、その辺りが不明です。

あと、AWSが提供している他のサービスとの連携・・・を Perl でやるには?とか。

aws-sdk-perl

AWS::Lambda とか?

その辺りは勉強進めていきたいですね。

何か良い資料ある方、あるいは教えてやろうという方お待ちしてます。

感想

「何も Perl でやらなくても良いのでは?」

「メモリで課金されるし、Perl 使うと高くつくのでは」

「標準で提供されている言語でやるのが楽なのでは」

などなど、やりながらいろいろ思ったんですが、何やかんやで「好奇心」と 1 週間前の自分に教えてあげたいよねー、というのが推進力でした。

Perl でうるう年判定

最近こんなツイートを見かけました。

ってわけで、このブログでうるう年の判定とか書いてたよなぁ・・・と思って検索したら書いてなかったという。あれー?

Perl でうるう年判定

条件分岐を教えるネタというと FizzBuzz なんですが、うるう年判定もいいかもしれないですね。

以下、wikipedia から拾ってきたうるう年判定の条件です。

  1. 400で割り切れる場合は閏年
  2. 400で割り切れず、100で割り切れる場合は平年
  3. 条件 1. と 2. を両方とも満たさない(400で割り切れず、かつ100でも割り切れない)場合、4で割り切れれば閏年、そうでなければ平年

ja.wikipedia.org

素直に書いてみました。

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

my $year = 1900;

# 1. 400で割り切れる場合は閏年
# 2. 400で割り切れず、100で割り切れる場合は平年
# 3. 条件 1. と 2. を両方とも満たさない(400で割り切れず、かつ100でも割り切れない)場合、4で割り切れれば閏年、そうでなければ平年

if ( $year % 400 == 0 ) {
    print "うるう年!\n";
}
elsif ( $year % 100 == 0 ) {
    print "平年\n";
}
elsif ( $year % 4 == 0 ) {
    print "うるう年!\n";
}
else {
    print "平年\n";
}

1 行でうるう年判定

検索してたら、東工大にいらっしゃる出口弘先生が神戸女学院大学にいた頃のページが出てきました。

wwws.kobe-c.ac.jp

C言語で解くうるう年の問題(閏年の判定)がのってます。

そこにこんな一文が

isleap関数をif文を1個で実現してみよ

となると、論理演算子です。Wikipedia にも載ってるこれを使います

また、閏年の規則は次の一つだけの論理式に読み替えることもできる。

(400で割り切れる)または(100で割り切れずかつ4で割り切ることができる)ならば閏年、そうでなければ平年

if ( ( $year % 400 == 0 && $year % 100 != 0 ) || $year % 4 == 0 ) {
    print "うるう年!\n";
}
else {
    print "平年\n";
}

1 個で書けというのがあるなら 1 行でかけってのもあるかなぁ、と書いてみました。三項演算子の重ねがけです。

print $year % 400 == 0 ? "うるう年" : $year % 100 == 0 ? "平年" : $year % 4 == 0 ? "うるう年" : "平年";

見やすく改行してみるとこうです。1 行じゃなくなってしまいますが・・・

print $year % 400 == 0 ? "うるう年"
    : $year % 100 == 0 ? "平年"
    : $year % 4 == 0   ? "うるう年"
    :                    "平年";

うるう年についての忘れられない話

2018年に開催された 設計Night2018 powered by Classi での @magnoria さんのこのトーク。2月29日生まれの人は平年のいつ歳を取るのか?とか知らなくてへぇ〜!ってなったトークでした。

logmi.jp

ところで

「最近こんなツイートを見かけました」から始まるのって定型文ぽくて、昔そういう短編みたなぁ・・・ってちょっと記憶さぐったら、夏目漱石の「夢十夜」でした。

漱石先生は「こんな夢を見た。」から書き始めてますね。しっかし激エモだなぁ・・・

「死んだら、埋めて下さい。大きな真珠貝で穴を掘って。そうして天から落ちて来る星の破片を墓標に置いて下さい。そうして墓の傍に待っていて下さい。また逢いに来ますから」

www.aozora.gr.jp

Perl で気象庁の非公式 API を叩いて天気情報を取得する

長いこと無料で天気情報APIを提供してくれていた Livedoor Weather さんがサービスを終えて以来、お手軽に利用できる天気情報APIがなかったのですが、最近こんなツイートを見かけました。

気象庁が提供する API ?非公式とはいえ、公式中の公式ともいえる気象庁が!

そして、それを受けて "やじうまの杜" さんが記事にしてくれました。

forest.watch.impress.co.jp

早速、 Perl で使ってみようというわけです。

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

use HTTP::Tiny;
use JSON qw/decode_json/;

my $ht = HTTP::Tiny->new();
my $res = $ht->get("https://www.jma.go.jp/bosai/forecast/data/overview_forecast/130000.json");

my $content = $res->{content};
my $decoded_json = decode_json($content);

print $decoded_json->{headlineText};

# 伊豆諸島南部では強風に、伊豆諸島、小笠原諸島では高波に、東京地方では空気の乾燥した状態が続くため、火の取り扱いに注意してください。

おおー、あっさり。

このスクリプトPerl 入学式の内容に以下を加えた内容です。

  • モジュールの使い方
  • リファレンスから情報を取り出す方法

エンドポイントが https なので、環境構築としては、SSL のライブラリとモジュールが必要です。

Windows のバッチファイルの中に書かれた Perl スクリプトを実行する

さて、Windows 環境で Perl と仲良くする!仲良くなりたい!って昨今です。

まぁ、仲良くする目的は仕事を楽にして twitter 眺める時間を捻出するとか、早く寝るとか、そういう真の目的のためですが・・・

前回は「バッチファイルから Perl を呼び出す」ってのやりました。

sironekotoro.hateblo.jp

そういえば

Perl 入学式でおなじみの Web フレームワークである Mojolicious::Lite ですが、Windows 環境にインストールして利用することも可能です。

もちろん、試験用のサーバ morboWindows で動きます。

そして、この morbo はバッチファイル morbo.bat なのです!

where コマンドで morbo の場所を探すとこんな感じです。

C:\Users\sironekotoro>where morbo
C:\Strawberry\perl\bin\morbo
C:\Strawberry\perl\bin\morbo.bat

ほんで、この C:\Strawberry\perl\bin\morbo.bat をメモ帳で開いてみると・・・最初の十数行こそバッチファイルですが、途中から Perl がそのまま書かれてますね!

コードはこちら、クリックで展開

@rem = '--*-Perl-*--
@set "ErrorLevel="
@if "%OS%" == "Windows_NT" @goto WinNT
@perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9
@set ErrorLevel=%ErrorLevel%
@goto endofperl
:WinNT
@perl -x -S %0 %*
@set ErrorLevel=%ErrorLevel%
@if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl
@if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH.
@goto endofperl
@rem ';
#!perl
#line 16
use Mojo::Base -strict;

use Mojo::Server::Morbo;
use Mojo::Util qw(extract_usage getopt);

getopt
  'b|backend=s' => \$ENV{MOJO_MORBO_BACKEND},
  'h|help'      => \my $help,
  'l|listen=s'  => \my @listen,
  'm|mode=s'    => \$ENV{MOJO_MODE},
  'v|verbose'   => \$ENV{MORBO_VERBOSE},
  'w|watch=s'   => \my @watch;

die extract_usage if $help || !(my $app = shift);
my $morbo = Mojo::Server::Morbo->new;
$morbo->daemon->listen(\@listen) if @listen;
$morbo->backend->watch(\@watch)  if @watch;
$morbo->run($app);

=encoding utf8

=head1 NAME

morbo - Morbo HTTP and WebSocket development server

=head1 SYNOPSIS

  Usage: morbo [OPTIONS] [APPLICATION]

    morbo ./script/my_app
    morbo ./myapp.pl
    morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl
    morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl
    morbo -w /usr/local/lib -w public -w myapp.conf ./myapp.pl

  Options:
    -b, --backend <name>           Morbo backend to use for reloading, defaults
                                   to "Poll"
    -h, --help                     Show this message
    -l, --listen <location>        One or more locations you want to listen on,
                                   defaults to the value of MOJO_LISTEN or
                                   "http://*:3000"
    -m, --mode <name>              Operating mode for your application,
                                   defaults to the value of
                                   MOJO_MODE/PLACK_ENV or "development"
    -v, --verbose                  Print details about what files changed to
                                   STDOUT
    -w, --watch <directory/file>   One or more directories and files to watch
                                   for changes, defaults to the application
                                   script as well as the "lib" and "templates"
                                   directories in the current working
                                   directory

=head1 DESCRIPTION

Start L<Mojolicious> and L<Mojolicious::Lite> applications with the L<Mojo::Server::Morbo> web server.

=head1 SEE ALSO

L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.

=cut
__END__
:endofperl
@set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%

ほほう!興味深い!!そしてわけわからん!!!

いや、うっすらはわかるけど、気になる、ってことでやっていきますよ。

何をやっていくって? 1 行ごとにコメントつけるんだよ!

頭の良い人はスラスラ推測つけて(しかもその推測あたってる)読み飛ばしていけるんだろうけど、うちはこう、アレがナニでダメな人なので・・・オランダ語医学書を解読した江戸期の人々みたいにやっていきます。

やっていく

@rem = '--*-Perl-*--

1 行目はバッチファイルのコメント行ですね。

ちなみに、先頭に @ つけるとコマンドプロンプトで結果が非表示になります。すっきり。でもデバッグの時は外すとよいです。

@set "ErrorLevel="

続いて、環境変数を宣言。= イコール 込みの変数名?ってなんか変ですね

@if "%OS%" == "Windows_NT" @goto WinNT

実行環境の OS の種類を判別して、"%OS%" == "Windows_NT" であれば goto でラベル WinNT に飛ばしています。

なお、Windows10 で実行すると Windows_NT なります。なので、Windows10 環境だとここからラベル WinNT まで飛びます

C:\Users\sironekotoro>echo "%OS%"
"Windows_NT"

うちが動かしているのは Windows10 上なので、以下の 3 行は関係ないのですが念のため・・・

@perl -x -S "%0" %1 %2 %3 %4 %5 %6 %7 %8 %9

Perl-x-Sコマンドラインオプションをつけて、バッチファイルの引数のプレースホルダ 10 個つけて起動してます。

では -x とは?ここは公式の日本語化をやってる perldoc.jp さんから

perldoc.jp

-x directory メールのような大きな無関係のASCII テキストのかたまりの中に プログラムが埋め込まれている事を Perl につたえます。 最初の #! で始まり、"perl" という文字列を含む行までの、 先行するゴミは捨てられます。 その行にある意味を持つスイッチは適用されます。 directory が指定されると、Perl はプログラムの実行前に、 そのディレクトリに移ります。 -x スイッチは先行するゴミの処分を制御するだけです。 プログラムの後に無視すべきゴミがある場合には、 END でプログラムを終了する必要があります (その、後に続く ゴミの一部または全部は、必要に応じて DATA ファイルハンドルを通して、 そのプログラムで処理する事ができます)。

へー! Perl って他のテキストファイルとかに埋め込むことできるんですね!知らんかった。というか、この morbo のバッチが確かにそうだわ。なるほどう!

つまり、ここで #!perl から __END__ までの Perl スクリプトが動くと。

バッチファイルに書いてある Perl が動く仕組みはこれだったのね。

つぎに -S とは?

Perl がプログラムを探すときに環境変数 PATH を参照するようにします (プログラム名がディレクトリセパレータを含むときを除きます)。

このあともすごい長い、というか濃い解説が続きますが、Windows の PATH を読んでくれる、ってことね(ざっくり理解)。

@set ErrorLevel=%ErrorLevel%

%ErrorLevel% は直前のコマンドの実行結果がどうであったか、コマンドの終了コードを取得するもの。それを環境変数 ErrorLevel=なし)に代入している。これは 2 行目で宣言した ErrorLevel= とは異なる変数。

@goto endofperl

そして、endofperl のラベルがあるところ(末尾から2行目)まで飛ぶ、と。

まだまだやっていく

:WinNT

WinNT と判定された 3 行目からこのラベルに移動してくる。

@perl -x -S %0 %*

-x-S は既に調べた。で、引数のところが %0 %* と短くなってる。既に書いたけど、バッチファイル中の Perl をここで実行する。

@set ErrorLevel=%ErrorLevel%

ここも前述なので飛ばす。と言いつつ書くと、上の perl -x -S ... の終了コードを代入している。

morbo は ctrl - c で止めるけど、その終了コードをここで拾っているってことなのかな?

@if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl

この %COMSPEC%コマンドプロンプト の本体、 cmd.exe の本体パスとのこと。

if NOT なので、標準のパス以外だったら終了のラベル endofperl に飛ばしてる。

@if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH.

Perl を実行した結果の終了コードが 9009 の場合には Perl が入っていないと判断する、ってのはわかる。

ググってみたりすると、 9009 ってのはファイルが見つからない時のエラーコードらしい。ほうほう。

@goto endofperl

エラーが出たら、endobperl ラベルへ〜

@rem ';

コメントで ; のみだけど、これは区切りって意味かな?

で、ここからは Perl 。引数を受け取って、morbo を起動してますね。ほんで =encoding utf8 からは POD で解説と。

#!perl
#line 16
use Mojo::Base -strict;

use Mojo::Server::Morbo;
use Mojo::Util qw(extract_usage getopt);

getopt
  'b|backend=s' => \$ENV{MOJO_MORBO_BACKEND},
  'h|help'      => \my $help,
  'l|listen=s'  => \my @listen,
  'm|mode=s'    => \$ENV{MOJO_MODE},
  'v|verbose'   => \$ENV{MORBO_VERBOSE},
  'w|watch=s'   => \my @watch;

die extract_usage if $help || !(my $app = shift);
my $morbo = Mojo::Server::Morbo->new;
$morbo->daemon->listen(\@listen) if @listen;
$morbo->backend->watch(\@watch)  if @watch;
$morbo->run($app);

=encoding utf8

=head1 NAME

morbo - Morbo HTTP and WebSocket development server

=head1 SYNOPSIS

  Usage: morbo [OPTIONS] [APPLICATION]

    morbo ./script/my_app
    morbo ./myapp.pl
    morbo -m production -l https://*:443 -l http://[::]:3000 ./myapp.pl
    morbo -l 'https://*:443?cert=./server.crt&key=./server.key' ./myapp.pl
    morbo -w /usr/local/lib -w public -w myapp.conf ./myapp.pl

  Options:
    -b, --backend <name>           Morbo backend to use for reloading, defaults
                                   to "Poll"
    -h, --help                     Show this message
    -l, --listen <location>        One or more locations you want to listen on,
                                   defaults to the value of MOJO_LISTEN or
                                   "http://*:3000"
    -m, --mode <name>              Operating mode for your application,
                                   defaults to the value of
                                   MOJO_MODE/PLACK_ENV or "development"
    -v, --verbose                  Print details about what files changed to
                                   STDOUT
    -w, --watch <directory/file>   One or more directories and files to watch
                                   for changes, defaults to the application
                                   script as well as the "lib" and "templates"
                                   directories in the current working
                                   directory

=head1 DESCRIPTION

Start L<Mojolicious> and L<Mojolicious::Lite> applications with the L<Mojo::Server::Morbo> web server.

=head1 SEE ALSO

L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.

=cut
__END__
:endofperl

で、ここが終着点のラベル endofperl と。最終的にはこのラベルにくる、はず?

@set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%

さーて一気にわからなくなった。

ええっと、バッチファイルでの & 、これは commandA & commandB と書いた場合に、commandA を実行したあとに commandB を実行する、というイディオムらしい。

ふむふむ。

この & の右側は _undefined_label_ これはわからんけど、定義してないラベルに @goto しろってことかな。

で、 || はさっきの & とは異なって、 commandC || commandD となり、 commandC が 終了結果が真の場合には commandD は実行しない、と。

@"%COMSPEC%" /d/c @exit %ErrorLevel%

で、commandC が偽だった場合にはこれが実行される。

%COMSPEC%コマンドプロンプト のパスなので、そこに /d/c オプションをつけて実行し、@exit すると。

つまり、 commandX & commandY || commandZ ってことかな?ええと、これは左?右?どっちから評価されるんだ?

・・・とりあえず、ながかったー!

バッチファイル の中で Perl をうごかす!

長く書いてきたのは全て自分のため・・・ですね!

ってことで、morbo.bat 参考にして作ったバッチファイルがこちらです。

@perl -x -S %0 %*
@set ErrorLevel=%ErrorLevel%
@if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" @goto endofperl
@if %ErrorLevel% == 9009 @echo You do not have Perl in your PATH.
@goto endofperl
@rem この下からPerl ;
#!perl
use strict;
use warnings;

my $str = "hello bat file!";
print "$str\n";

print "@ARGV";  # コマンドライン引数があれば表示される

# Perl はこの下の __END__ まで
__END__
:endofperl
@set "ErrorLevel=" & @goto _undefined_label_ 2>NUL || @"%COMSPEC%" /d/c @exit %ErrorLevel%

参考にしたページ

Perl については文中にページへのリンク書いてきましたが、コマンドプロンプト に関してはこちらのページを多く参考にさせてもらいました。ありがとうございました。

www.pg-fl.jp

Perl入学式 オンライン 2020 第5回お疲れ様でした

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

講義に利用したスライド・動画類は以下 Perl 入学式の公式サイトで公開しています。参加された方も、参加できなかった方も、ぜひ復習に使ってください。

appslideshare.tugougaii.site

問題の意味がわからない、とか、このような解答例はどうだろう?という方は Discord の Perl 入学式チャンネル(招待コード)や、twitterハッシュタグ #Perl入学式 をつけて聞いてみてください。

スライド中の練習問題ですが、私の回答例をおいておきます。

github.com

Perl入学式 オンライン 2020 第5回

今回は以下の項目について学習しました。

  • ハッシュを操作する関数
  • サブルーチン

これで、スカラー変数($scalar)、配列(@array)、ハッシュ(%hash)という基本の変数を学習したことになります。

これらの変数に値を格納し、取り出し、削除し、追加する術を学びました。

自分の作りたいプログラム、欲しいものを作るにはこれらだけでは足りないと思うのですが、これらの基本無くしては到達することは難しいでしょう。

何度も立ち返って思い出しつつ学んでみてください。

Perl入学式 オンライン の後に何をすれば良いの?

一昨年以前の集合講義形式で行っていたテキストを参考にしてみてください。

(注意)スライド同期くんの操作は左右で章の切り替え、上下でページの切り替えとなっています

appslideshare.tugougaii.site

オンライン版では、2019年カリキュラムの第3回までをやったことになります。

この第3回以降に学ぶ「正規表現」「リファレンス」は今後の Perl を学んでいくのに欠かせない内容になります。

正規表現」によるテキスト処理は Perl が非常に有用な分野です。

「リファレンス」は「スカラー変数の中にハッシュを入れる」など、より複雑なデータ構造を表現するために必須の内容です。

自分の経験からいって、初心者が自習するには難しい内容とは思うのですが(教えていてもそう思う)、挑戦してほしい分野です。

不明点があればコードを添えて discord で質問してください。

お待ちしております。

Perl入学式 オンライン 2021

コロナ禍から始まった Perl 入学式オンラインですが、2021年4月からの春から新年度の開講予定です。

2021年は Perl のメジャーバージョンアップ( 5 → 7 )も控えているイベントイヤーです。

Wandbox ではなく、環境構築を手元のパソコンで行うなど、新たな挑戦を行います。

Perl 入学式は2周、3周の受講を歓迎します。私自身も2周受講しました。

興味ある方の受講をお待ちしています。

1 行増えただけじゃん

長い前置き

平穏な時代

経理のお仕事で、仕訳というのがあります。複式簿記に則ってお金や債権・債務の流れを記録していきます。

いろいろなやり方があると思うのですが、うちの場合、 Google Sheet でマスターデータを書いておいて、それを Google Apps で処理して会計ソフト(弥生会計)取り込み用の csv ファイルにする、という方法をとっています。

マスタデータはこんな感じです。

名前 発生 金額 手数料負担 支払手数料 摘要
白猫商事 2021.12 10,000 得意先 176 猫用ホットカーペット

ひとつの振込先につき支払いはひとつ、という前提があり、こんなフローで仕訳 csv を作成します。

振り込み手数料は 30,000 円未満は 176円、30,000 円以上は275円です。

  1. マスターデータから 1 行取り込む
  2. 銀行手数料が得意先負担の場合には、以下の2行を作って仕訳シートに追加する
    1. 支払額から支払い手数料を算出する
    2. 支払額から支払手数料を引いた金額を支払額にする
    3. 支払手数料の行を追加する
  3. 銀行手数料が当方負担の場合には、金額をそのままに1行作って仕訳シートに追加する

データ構造としてはこんな感じです

[
    { 名前 => 白猫商事, 金額 => 10000, 手数料負担 => 得意先, 支払手数料 => 176, ... },
]

で、仕訳を作るとこんな感じです。

勘定科目 金額 勘定科目 金額 摘要
未払金 10,000 普通預金 9,824 白猫商事/2021.12/猫用ホットカーペット
支払手数料 176 白猫商事/2021.12/振込手数料

相手への支払い義務のあるお金(未払金)を、普通預金から出金して支払うという仕訳です。

ただし、白猫商事は振込手数料を先方が負担するので、手数料分を引いた金額を振込みます。

崩れる前提

ひとつの振込先につき支払いはひとつ、という前提があり、仕訳csvを作る時は

はい。ある日突然、一つの振込先に対する支払いの行を複数にすることになります。

名前 発生 金額 手数料負担 支払手数料 摘要
黒猫農業 2021.12 10,000 得意先 176 マタタビ
黒猫農業 2021.12 30,000 得意先 176 高級マタタビ

そして、振り込まれる側(この場合は黒猫農業)としては、一括で振り込んで欲しい(一つの支払いごとに支払い手数料を負担したくない)のも当然ですね。

最終的にはこういう仕分けになります。振り込み額が30,000円を超えるので、銀行手数料が変更になります。

勘定科目 金額 勘定科目 金額 摘要
未払金 10,000 普通預金 39,725 黒猫農業/2021.12/マタタビ
未払金 30,000 黒猫農業/2021.12/高級マタタビ
支払手数料 275 黒猫農業/2021.12/振込手数料

ぎゃー!

こうなってしまうと、マスターデータから1行読み込んで処理するという方法は取れません。

マスターデータ側で合算して 1 行にしてしまうのが一番簡単ですが、会計ソフト上でマタタビ・高級マタタビでそれぞれの費用を分析する場合には不向きになります。

となると、このような感じで処理することになります。

  1. ハッシュのデータ構造にする。key を名前に、 value には配列を格納するハッシュ(辞書型)のデータ構造を作る
  2. 同じ key の場合には、value に push する
  3. 出来上がったデータ構造を key ごとに処理する
    1. value の要素が 1 つだけの場合(白猫商事、黒猫物産の場合)は従来通り
    2. value の要素が複数の場合
      1. 支払額を合算
      2. 支払手数料も合算した額で算出
      3. 合算した支払額を1行目に集約
      4. 2行目以降は未払金のみ記載、支払額欄は空欄
      5. 最後の行に支払手数料を入れる

となります。この処理を行うためには、こんなデータ構造にする必要があります。

{
    茶虎農業 => [ 
        { 金額 => 10000, 手数料負担 => 得意先, ... }, 
        { 金額 => 30000, 手数料負担 => 得意先, ...  },
     ],
}

ここまでデータ構造の当たりがつけば、要件を満たすことができそうです。

というわけで

この「ぎゃー!」ってなってから実装し直すまで結構時間がかかった(試行錯誤で 1 日半くらい)ので記念に書いてみました。

こうやって書き出して思い出してみると、たいしたことないところで悩んでたなー、とか思うんですが、まぁ、喉元過ぎれば熱さ忘れるってやつですね。

FC2 ブログに記事を投稿する Perl モジュール作った

はい。タイトルのまんまです。

github.com

元々は、ちょっと表に出せないような欲望から始まったんですが、その途中でブログに投稿できるモジュールが欲しいなー、ということになり、気づいてみると欲望置き去りにして完成してたのがこのモジュールです。

目的と手段がよくすり変わるという自覚はあります。

手段たーのしー!

今回は目的別にモジュール分けるってのをやってみました。こんなんです。

$ tree lib/WebService/FC2
lib/WebService/FC2
├── Blog
│   ├── Delete.pm
│   ├── Edit.pm
│   ├── Get.pm
│   ├── Media.pm
│   └── New.pm
└── Blog.pm

問題は全然テスト書いてないことです。

このモジュールは XML-RPC を使って各種の操作を行うのですが、その XML-RPC のモック?を用意しなきゃいけない?の?

ってところで時間切れというか気力がつきました。