سؤال

لدي مشروع بيرل كان لدي مشكلة فقط من خلال إجراء مكالمة حزمة دائرية. الرمز أدناه يوضح المشكلة.

عند تنفيذ ذلك، ستدعو كل حزمة إلى الآخر حتى يتم استهلاك جميع ذاكرة الكمبيوتر وتأمينها. أوافق على أن هذا تصميم سيء وأن المكالمات الدائرية مثل هذا لا ينبغي أن يتم في التصميم، ولكن مشروعي كبير بما فيه الكفاية وأود أن اكتشاف هذا في وقت التشغيل.

لقد قرأت عن الوظيفة الإضعاف والبيانات :: هيكل :: 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();

أفضل طريقة لتجنب هذا هو لا تفعل ذلك. وبعد بشكل أكبر، إذا كان عليك أن تكتب وظائف متكررة، فحاول عدم استخدام وظائف متعددة في سلسلة Recursion، ولكن ببساطة واحدة، لذلك لديك وقت أسهل في تتبع عقليا من حيث يجب أن تنهي مكالماتك.

فيما يتعلق بكيفية اكتشاف ما إذا كان هناك شيء من هذا القبيل يحدث، سيتعين عليك القيام بشيء بسيط مثل زيادة متغير مع عمق الإصابة وإنهاء (أو العودة) إذا تجاوز عمقك قيمة معينة. لكنك لا يجب أن تعتمد على ذلك، إنه يشبه كتابة while حلقة واستخدام زيادة هناك للتأكد من أن وظيفتك لا تنفد عن نطاق السيطرة. فقط لا تنتشر فوق مجموعة ما لم تعرف كيف ومتى تنتهي.

سؤال آخر ذي صلة سيكون ما تحاول إنجازه في المقام الأول؟

أقترح إجراء روتين يسمى شيئا مثل break_constressor_recursion () يستخدم المتصل () لفحص مكدس المكالمة مثل ذلك:

اكتشف طريقة ما هي الحزمة التي اتصل بي تماما.

ابحث عن بقية مكدس المكالمة إذا كانت هذه الطريقة نفسها في تلك الحزمة نفسها في أي مكان.

إذا كان الأمر كذلك، تموت () مع شيء مناسب.

ثم تقوم بإضافة مكالمة إلى break_constructor_recursion () في منشئتك. إذا تم استدعاء المنشئ من الداخل نفسه، فسوف يفسد.

الآن، هذا يمكن أن يرمي الإيجابيات الخاطئة؛ ليس من المستحيل أن يتم استدعاء المنشئ بشكل شرعي داخل نفسه. إذا كانت لديك مشكلات مع ذلك، فأنا أقول فقط أن تبحث عن بعض الاحداثيات الإضافية N من المنشئ قبل أن تحدد خطأ. إذا كان هناك 20 مكالمة للنظام :: اثنين :: جديد () على المكدس، والفرص التي لا تكررها منخفضة جدا.

الاستراحة الكلاسيكية على Recursion مزدوجة هي استخدام متغير الحالة لتحديد ما إذا كنت بالفعل داخل وظيفة:

{
    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جي أو العودة شائعة. إذا كنت تستخدم Perl 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 # بعد الموت

مع التحذيرات:

#!/usr/bin/perl 

use strict;
use warnings;

sub foo {
    foo(); 
}

foo();

-

$ perl script.pl recursion العميق على الروتين الفرعي "Main :: foo" في خط script.pl 7. ^ c # بعد الموت

دائما استخدام التحذيرات دائما.

use warnings FATAL => qw( recursion );

#!/usr/bin/perl 

use strict;
use warnings FATAL => qw( recursion );

sub foo {
    foo(); 
}

foo();

-

$ Perl Script.pl Ream Recursion على الروتين الفرعي "Main :: Foo" في Script.pl Line 7. $
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top