Frage

Ich habe ein Perl-Projekt, bei dem ich gerade ein Problem hatte, als ich einen zirkulären Paketaufruf durchführte.Der folgende Code veranschaulicht das Problem.

Wenn dies ausgeführt wird, ruft jedes Paket das andere auf, bis der gesamte Speicher des Computers belegt ist und dieser abstürzt.Ich stimme zu, dass dies ein schlechter Entwurf ist und dass Zirkelaufrufe wie dieser nicht im Entwurf vorgenommen werden sollten, aber mein Projekt ist groß genug, dass ich dies gerne zur Laufzeit erkennen würde.

Ich habe über die Weaken-Funktion und Data::Structure::Util gelesen, aber ich habe keine Möglichkeit gefunden, zu erkennen, ob eine zirkuläre Paketladung vorliegt (ich gehe davon aus, weil bei jeder Iteration eine neue Kopie erstellt und gespeichert wird). in jeder Kopie des $this-Hashs).Irgendwelche Ideen?

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; 
} 
War es hilfreich?

Lösung

Hier haben auch einige Code. :)

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;
}

Diese werden als wie:

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.

könnten diese cpan, glaube ich. Wenn jemand irgendwelche Gedanken darüber hat, bitte teilen.

Andere Tipps

Die Tatsache, dass diese in separaten Paketen sind, hat nichts mit der Tatsache zu tun, dass diese unendlich läuft, alle verfügbaren Ressourcen verbrauchen. Sie anrufen zwei Methoden aus ineinander. Dies ist nicht kreisförmig Referenz, es ist Rekursion , die nicht die gleiche Sache. Insbesondere weaken werden Ihnen nicht helfen. Sie würden erhalten genau die gleiche Wirkung aus:

sub a {
    b();
}

sub b {
    a();
}

a();

Der beste Weg, dies zu vermeiden, ist nicht tun, . Sinnvoller, wenn Sie versuchen, rekursive Funktionen schreiben müssen nicht mehrere Funktionen in der Rekursion Kette zu verwenden, aber die man einfach, so dass Sie eine einfachere Zeit geistig haben die Verfolgung von wo Sie Ihre Anrufe beenden sollte.

In Bezug auf, wie zu erkennen, ob so etwas wie dies geschieht, würden Sie etwas tun müssen, einfach wie eine Variable mit Rekursionstiefe erhöhen und beenden (oder zurück), wenn Ihre Tiefe einen bestimmten Wert überschreitet. Aber Sie sollen wirklich nicht darauf verlassen müssen, dann ist es ähnlich wie bei einer while Schleife zu schreiben und mit einem Zuwachs sich um sicherzustellen, dass Ihre Funktion nicht außer Kontrolle läuft. Nur nicht einen Satz Rekursion über, wenn Sie wissen, wie und wann sie endet.

Eine weitere relevante Frage wäre, was Sie in erster Linie versuchen zu erreichen?

Ich schlage vor, eine Routine mit dem Namen break_constructor_recursion() zu erstellen, die caller() verwendet, um den Aufrufstapel wie folgt zu untersuchen:

Finden Sie heraus, welche Methode in welchem ​​Paket mich gerade aufgerufen hat.

Schauen Sie im Rest des Aufrufstapels nach, ob sich dieselbe Methode in demselben Paket irgendwo weiter oben befindet.

Wenn ja, die() mit etwas Passendes.

Anschließend fügen Sie in Ihren Konstruktoren einen Aufruf von break_constructor_recursion() hinzu.Wenn der Konstruktor aus sich selbst heraus aufgerufen wird, wird er explodieren.

Dies kann zu Fehlalarmen führen.Es ist nicht unmöglich, dass ein Konstruktor in sich selbst legitim aufgerufen wird.Wenn Sie damit Probleme haben, würde ich sagen, dass es einfach nach einigen N zusätzlichen Vorkommen des Konstruktors suchen soll, bevor es einen Fehler erkennt.Wenn es 20 Aufrufe von system::two::new() auf dem Stapel gibt, ist die Wahrscheinlichkeit, dass Sie nicht rekursiv sind, ziemlich gering.

Die klassische Pause auf doppelte Rekursion ist eine Zustandsgröße zu verwenden, um festzustellen, ob Sie bereits in einer Funktion sind:

{
    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;
    }
}

Sie können tun, was Sie wollen, wenn $in_a wahr ist, aber dieing oder Rückkehr ist üblich. Wenn Sie mit Perl 5.10 oder später können Sie die state Funktion verwenden, anstatt nisten die Funktion in einem eigenen Bereich:

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;

ohne Warnungen:

#!/usr/bin/perl 

use strict;

sub foo {
    foo(); 
}

foo();

-

$ perl script.pl 
^C # after death 

mit Warnungen:

#!/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 

Immer immer Warnungen verwenden.

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.
$ 
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top