何をしたの?
何と言ったら正しいのかよくわからないのですが、、、
Perlでクロージャを使って、プログラム内で使用するデータを次々に生成してくれるようなものを作りました。
数パターンのデータを自動的に返してくれるような感じと言うか、
雰囲気的には、変則的な桁繰上りをしてくれるような感じです。
コード
use strict;
use warnings;
my $get_status = sub {
use POSIX qw(floor);
use bignum;
my $count = 0;
my $skip = []; #スキップしたい回数目があれば、ここに整数値を配列で入れる
my $rule = [ #ルール名・最小値・加算値・最大値をハッシュの配列で持たせる
{ name => 'a', min => 1, step => 1, max => 2 },
{ name => 'b', min => 2, step => 1, max => 4 },
{ name => 'c', min => 0.4, step => 0.2, max => 8 },
{ name => 'd', min => 0.5, step => 0.1, max => 2 },
];
my $status = {};
my $maximum = 1;
foreach my $tmp_rule ( @{$rule} ){
$status->{pattern}->{$tmp_rule->{name}} = $tmp_rule->{min};
$tmp_rule->{roundup} = ( grep { $_->{length} } @{$rule} )
? do { my $tmp = 1; map { $tmp *= $_->{length} if( $_->{length} ); } @{$rule}; $tmp; }
: 1;
$tmp_rule->{length} = ( $tmp_rule->{max} - $tmp_rule->{min} ) / $tmp_rule->{step} + 1;
$maximum *= $tmp_rule->{length};
}
return sub {
while(1){
last if ( !( grep { $_ == $count } @{$skip} ) );
$count++;
}
foreach my $tmp_rule ( @{$rule} ){
$status->{count} = $count;
$status->{pattern}->{$tmp_rule->{name}} = floor( ($count / $tmp_rule->{roundup}) % $tmp_rule->{length} ) * $tmp_rule->{step} + $tmp_rule->{min};
}
$count++;
return ( $count >= $maximum+1 ) ? undef : $status;
};
}->();
my $params;
while( 1 ){
$params = $get_status->();
last if( !$params );
print $params->{pattern}->{d}, ', ', $params->{pattern}->{c}, ', ', $params->{pattern}->{b}, ', ', $params->{pattern}->{a}, ': ', $params->{count}, "\n";
}
exit;
実行結果
0.5, 0.4, 2, 1: 0
0.5, 0.4, 2, 2: 1
0.5, 0.4, 3, 1: 2
0.5, 0.4, 3, 2: 3
0.5, 0.4, 4, 1: 4
0.5, 0.4, 4, 2: 5
0.5, 0.6, 2, 1: 6
...(中略)...
2, 7.8, 4, 2: 3737
2, 8, 2, 1: 3738
2, 8, 2, 2: 3739
2, 8, 3, 1: 3740
2, 8, 3, 2: 3741
2, 8, 4, 1: 3742
2, 8, 4, 2: 3743
なんで必要だったの?
ループ(上コードの中では、最後のwhile)の中で計算してもいいのだけど、見た目がごちゃっとして、後々どこが何なのかわかりにくくなるな、と思ったのでした。
でも、サブルーチン切り分けていちいち引数で回数の値を渡すのもなんだかなぁ、かといってカウント値をblessして使いまわすのも変だなぁ、と思ったりもしたのでした。
使いどころは?
テストの際などに、決められたルールの上で自動的に値を出してくれるので、たまに役に立つかな? とか。
今回は、ループ内で値を変えながら何回も繰り返して、最終的に値ごとの計算結果を比較する、みたいな処理がしたくて、そこで使いたかったのでした。
使いどころは限られますが、割と汎用性はあるような気がしています。
クロージャ内で保持しているのは単純な整数値(上コードの$count)なので、割と取り回しもしやすいかな、と。
あとは、$skipに外部データから読み込むようにすれば、中断・再開にも少しだけ対応できてうれしいな、と思っています。
追記
なんだか実行結果に変なところがあったので、一度下書きに戻して修正しよう、と思ったら誤って記事ごと削除しちゃいましたので再投稿です。すみません。
変だった部分は、剰余絡みの部分で、オーバーフロー起こしておかしな数値をたたき出すことがあるようでした。
というか、なんか剰余を出す途中で処理が止まって、剰余ではなくて割り算の結果(の、小数点以下切り捨てされた値)が出てくることがありました。
ので、use bignum;を入れて解決しました。でも、何倍も遅くなっちゃいましたが、、、
あとは、後から気になった細かい部分をちょっと修正したりもしました。