遅延実行と仮実行(dry_run)について考えてみた

この記事は Perl 5 Advent Calendar 2016 の8日目の記事です。

昨日は @wakegisky さんの Perl の基礎知識で実用プログラミング 〜旅情編〜 でした。

データを追加するだけで動作がどんどん良くなる、凄い仕組みのプログラムですね。 必要は発明の母です。

さて、今日は関数のリファレンスを使った tips を書いてみたいと思います。

遅延実行と仮実行(dry_run)

シチュエーションは色々あると思いますが、実行結果を事前に確認したい、ということはよくあります。

コマンドとかの場合は、「–dry-run」のようなオプションがあって、そのオプションを付けると実際には何も変更しないけれども、実行した感じで結果が返ってくる。みたいな。

でも、これって結構面倒だと思いませんか?

最初からちゃんと考えて実装していれば問題ないですが、そうでもない場合は、実行しているところが色々分散していて、仮実行のときはここは変更しない、みたいなところを色々とif文などで避けなければいけない。

実際に実行する箇所を一つにまとめられたら楽なんじゃないかなと思いませんか?

関数のリファレンスで処理をまとめる

そこで登場するのが関数のリファレンスです。

実装のイメージとしてはこんな感じ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
use strict;
use warnings;
use feature 'say';

my @job;
push @job, sub { say 1 };
push @job, sub { say 2 };
say "hoge";
unshift @job, sub { say 3 };
for (@job) { &{$_} }

これを実行すると以下のようになります。

1
2
3
4
hoge
3
1
2

基本的な仕組みとしては、関数のリファレンスを push したり unshift したりして、配列に貯めておき、実行するときは関数のリファレンスをデリファレンスする、という感じです。

こうしておくと、実際の実行部分は、関数のデリファレンス部分にまとまります。

この仕組を利用すると、実際の書き込み処理は配列に貯めておいて、仮実行のときはこれを実行しない、というようにすることができます。

1
2
3
unless ($dry_run) {
    for (@job) { &{$_} }
}

$dry_run が真の場合、以下のように出力されます。

1
hoge

ということで、処理が各所に点在していても、仮実行の対象にしたい処理はグローバルな変数などを一つ用意して、そこにジョブ(関数のリファレンス)を貯めておき、仮実行でない場合だけ実行するようにすることができます。

こういった仕組みを使うと、例えば Teng の場合は、値をセット(set, set_columnsなど)だけして、実際の更新処理(update)は関数のリファレンスで貯めておき、dry_runのときはupdateの処理を捨てることで、限りなく本番に近い仮実行ができるようになります。

CGIにも関数のリファレンスを

話は変わりますが、古き良きCGIは、スクリプトの最初にまずHTTPヘッダを出力したりしています。

テンプレートを使っていない場合は、その次にHTMLのヘッダを出力したりしているのではないでしょうか。

そうして順に処理していったところで何らかのエラーが発生した場合、突然エラーメッセージが出力されてHTMLがちぎれた状態になったりするんですよね。

でも、そういうのを修正するのは面倒なので、なかなか手を付けられないのではないかとおもいます。

そんな時に、先程の仕組みを使えば、あまり沢山のコードを書き換えなくてもよくなります。

print で出力しているところをグローバルな変数に関数のリファレンスとして貯めておき、エラーのときは、それまで貯めておいた処理を捨てて、エラーだけを出力する、なんてことができるようになりますよ。

最後に

2017年3月4日(土)に YAPC::Japan と Kansai.pm がコラボした、「YAPC::Kansai 2017 OSAKA」を開催します。

まだまだ未定なことが多いですが、今から予定を空けておいてください。

明日は skaji さんで、「cpmについて書きます」のようです。

また、アドベントカレンダーには、まだ空きがありますので、何か書いてみたい方は是非ご参加ください!!

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy