Pergunta

Eu tenho um projeto Perl foram Eu só tinha um problema fazendo uma chamada pacote circular. O código a seguir demonstra o problema.

Quando este é executado, cada pacote vai chamar o outro até que toda a memória do computador é consumido e ele trava. Concordo que este é um projeto ruim e que as chamadas circulares como este não deve ser feito no projeto, mas meu projeto é suficientemente grande que eu gostaria de detectar isso em tempo de execução.

Eu li sobre a função enfraquecer e Data :: Estrutura :: Util, mas eu ainda não descobri uma maneira de detectar se existe uma carga circular pacote (eu sou supor, porque uma nova cópia está sendo feito em cada iteração e armazenados em cada cópia do esse hash $). Alguma idéia?

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; 
} 
Foi útil?

Solução

Aqui, tem algum código também. :)

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

Estes são chamados como:

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.

Pode CPAN estes, eu acho. Se alguém tem alguma opinião sobre isso, por favor, compartilhe.

Outras dicas

O fato de que estes estão em pacotes separados não tem nada a ver com o fato de que este é executado infinitamente, consumindo todos os recursos disponíveis. Você está chamando dois métodos de dentro um do outro. Este não é referência circular, é recursão , que não é a mesma coisa. Em particular, weaken não irá ajudá-lo em tudo. Você deseja obter exatamente o mesmo efeito de:

sub a {
    b();
}

sub b {
    a();
}

a();

A melhor maneira de evitar isso é não faça isso . Mais útil, se você tem que escrever funções recursivas tente não usar múltiplas funções na cadeia de recursão, mas simplesmente a um, então você tem um tempo mais fácil mentalmente manter o controle de onde as chamadas devem terminar.

Quanto à forma de detectar se algo como isso está acontecendo, você teria que fazer algo simples como incremento de uma variável com a sua profundidade de recursão e terminar (ou retorno) se sua profundidade ultrapassa um determinado valor. Mas você realmente não deveria ter que contar com isso, é semelhante a escrever um loop while e usando um incremento lá para se certificar de sua função não é executado fora de controle. Só não recurse mais de um conjunto a menos que você sabe como e quando ele termina.

Outra questão relevante seria o que você está tentando realizar, em primeiro lugar?

Sugiro fazer uma rotina chamado algo como break_constructor_recursion () que usa chamador () para examinar a pilha de chamadas assim:

Saiba o que o método em que pacote chamado apenas de mim.

Procure o resto da pilha de chamadas de ver se esse mesmo método no mesmo pacote está em qualquer lugar mais acima.

Se assim for, die () com algo apropriado.

Em seguida, você adicionar uma chamada para break_constructor_recursion () em seus construtores. Se o construtor está sendo chamado de dentro de si, ele vai bombardear fora.

Agora, este pode jogar falsos positivos; não é impossível para um construtor para ser legitimamente chamado dentro de si. Se você tiver problemas com isso, eu diria que apenas tem que olhar para algumas N ocorrências adicionais do construtor antes que identifica um erro. Se houver 20 chamadas para sistema :: dois :: new () na pilha, as chances de que você não está recursão são muito baixos.

A ruptura clássico sobre a dupla recursão é usar uma variável de estado para determinar se você já está dentro de uma função:

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

Você pode fazer o que quiser, se $in_a é verdade, mas dieing ou retornando é comum. Se você estiver usando Perl 5.10 ou mais tarde você pode usar a função state vez de nidificação a função em seu próprio escopo:

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;

sem avisos:

#!/usr/bin/perl 

use strict;

sub foo {
    foo(); 
}

foo();

-

$ perl script.pl 
^C # after death 

com avisos:

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

Sempre use sempre os avisos.

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.
$ 
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top