できるかを検出しrecursingパッケージにPerl?
質問
私は、Perlプロジェクトがこれだった問題による円形のパッケージ。以下のコードの問題です。
これを実行すると、各パッケージはそのまですべてのメモリのコンピュータを消費するとロックしています。同意することは悪いデザインが円形のようなことは避けなければならないのデザインが私のプロジェクトは十分に大きいと思うようにな検出することです。
いては下記を参照して下さい。弱める機能とデータ構造::Utilがっていないのに把握方法の検出が循環している場合はパッケージ荷重に思考で新しいコピーはこれについて繰り返し処理毎に蓄積され、各コピーのこのハッシュ).そのアイデア?
use system::one;
my $one = new system::one();
package system::one;
use strict;
use system::two;
sub new {
my ($class) = @_;
my $this = {};
bless($this,$class);
# attributes
$this->{two} = new system::two();
return $this;
}
package system::two;
use strict;
use system::one;
sub new {
my ($class) = @_;
my $this = {};
bless($this,$class);
# attributes
$this->{one} = new system::one();
return $this;
}
解決
ここでは、あまりにもいくつかのコードを持っています。 :)
sub break_recursion(;$) {
my $allowed = @_ ? shift : 1;
my @caller = caller(1);
my $call = $caller[3];
my $count = 1;
for(my $ix = 2; @caller = caller($ix); $ix++) {
croak "found $count levels of recursion into $call"
if $caller[3] eq $call && ++$count > $allowed;
}
}
sub check_recursion(;$) {
my $allowed = @_ ? shift : 1;
my @caller = caller(1);
my $call = $caller[3];
my $count = 1;
for(my $ix = 2; @caller = caller($ix); $ix++) {
return 1
if $caller[3] eq $call && ++$count > $allowed;
}
return 0;
}
これらは、同様に呼ばれています:
break_recursion(); # to die on any recursion
break_recursion(5); # to allow up to 5 levels of recursion
my $recursing = check_recursion(); # to check for any recursion
my $recursing = check_recursion(10); # to check to see if we have more than 10 levels of recursion.
私が思うに、これらをCPAN可能性があります。誰もがそのことについてどんな考えを持っている場合は、共有してください。
他のヒント
これらが別々のパッケージには何もすべてのことを走る無限の間で利用可能なすべて資源です。い呼び方から内します。このな循環参照で 再帰, は、同じものではありません。特に、 weaken
いきます。い全く同じ効果:
sub a {
b();
}
sub b {
a();
}
a();
最良の方法これを避けるためには んでいること.よりusefullyが好きなの再帰的機能のみを使用しない複数の機能を再帰チェーンが簡単にできやすい時間の精神的にも身体の安全を確保するためにお客様の電話は停止されます。
としてどのように検出するかのようなことが起きていないように値の変数と再帰深度および終了(または戻り)の場合の深さを超える値です。がまんしていないのと同様書 while
ループ利用の増加が確認機能が動作しない。なrecurse一定のない限り、ノウハウで終了します。
他の関連する質問があけましておめでとうございまいに達成すべきでしょうか。
私はそうのようなコールスタックを調べるために)発信者(使用break_constructor_recursionのようなもの(と呼ばれるルーチン)を作ることをお勧め
ちょうど私に何と呼ばれるパッケージ内のどの方法を見つけるます。
同じパッケージ内に同じ方法でどこでもさらにアップしている場合は、見コールスタックの残りの部分を検索します。
もしそうであれば、適切な何かを)(死にます。
次に、あなたのコンストラクタで()break_constructor_recursionの呼び出しを追加します。コンストラクタは、それ自体の内部から呼び出されている場合は、それが出て爆撃ます。
さて、これは偽陽性を投げることができます。コンストラクタは、合法的に、それ自体の内部で呼び出されることは不可能ではありません。あなたはそれで問題が発生した場合、私はそれがエラーを特定する前に、ちょうどそれがコンストラクタのいくつかのN追加の出現を探していると思います。スタック上のシステム:: 2 ::新しい()への20回の呼び出しがある場合、あなたは再帰されていない可能性はかなり低いです。
ダブル再帰の古典的なブレークは、関数の内部ですでにあるかどうかを判断するために、状態変数を使用することです。
{
my $in_a;
sub a {
return if $in_a; #do nothing if b(), or someone b() calls, calls a()
$in_a = 1;
b();
$in_a = 0;
}
}
あなたは$in_a
が真であるが、die
ingまたは返却が共通であれば、あなたがやりたいことができます。あなたが5.10以降を使用している場合は、代わりに独自のスコープ内の関数をネストstate
機能を使用することができます:
sub a {
state $in_a;
return if $in_a; #do nothing if b(), or someone b() calls, calls a()
$in_a = 1;
b();
$in_a = 0;
}
use warnings;
警告なします:
#!/usr/bin/perl
use strict;
sub foo {
foo();
}
foo();
-
$ perl script.pl ^C # after death
警告してます:
#!/usr/bin/perl
use strict;
use warnings;
sub foo {
foo();
}
foo();
-
$ perl script.pl Deep recursion on subroutine "main::foo" at script.pl line 7. ^C # after death
常に常に警告を使用します。
use warnings FATAL => qw( recursion );
#!/usr/bin/perl
use strict;
use warnings FATAL => qw( recursion );
sub foo {
foo();
}
foo();
-
$ perl script.pl Deep recursion on subroutine "main::foo" at script.pl line 7. $