Как я могу обнаружить вызовы пакета рекурсивного вызова в Perl?

Я думаю, что один из способов добиться того, что вы пытаетесь сделать, это сделать следующее:

var query= PagesContext.Versions.Where(ve=>ve.ContentStatusID == 2)
                                .SelectMany(ve=>ve.View.Roles
                                                  .Where(r=>r.IsAdminRole == false)
                                                  .Select(r=> new RestrictedPage
                                                                   {
                                                                     ViewID = ve.ViewID,
                                                                     Title = ve.Title,
                                                                     RoleID = r.ID,
                                                                     Role = r.Role1,
                                                                     VersionID = ve.VersionID
                                                                   })).ToList();

В вашем случае таблица соединений не отображается напрямую, это скрыть, так что одно решение для получения данных связано вам нужно использовать метод расширения SelectMany. Сначала примените условие к одному из концов запроса, в моем примере это было Versions, а затем примените SelectMany, которое будет генерировать внутреннее соединение между обеими таблицами и сгладить результат в одной коллекции.

Обновление

Я думаю, что проблема в том, что версия и View на самом деле не связаны напрямую с вашей БД, поэтому вы собираетесь сделать явное внутреннее соединение:

var query= PagesContext.Versions.Where(ve=>ve.ContentStatusID == 2)
                                .Join( PagesContext.Views, ve=>ve.ViewId, v=>v.ID,(ve,v)=>v)
                                .SelectMany(v=>v.Roles
                                                  .Where(r=>r.IsAdminRole == false)
                                                  .Select(r=> new RestrictedPage
                                                                   {
                                                                     ViewID = ve.ViewID,
                                                                     Title = ve.Title,
                                                                     RoleID = r.ID,
                                                                     Role = r.Role1,
                                                                     VersionID = ve.VersionID
                                                                   })).ToList();
6
задан chaos 10 April 2009 в 19:55
поделиться

5 ответов

Здесь тоже есть код. :)

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 это, я думаю. Если у кого-то есть мысли по этому поводу, пожалуйста, поделитесь.

4
ответ дан 8 December 2019 в 17:27
поделиться

The fact that these are in separate packages has nothing at all to do with the fact that this runs infinitely, consuming all available resources. You're calling two methods from within one another. This isn't circular reference, it's recursion, which is not the same thing. In particular, weaken won't help you at all. You'd get exactly the same effect from:

sub a {
    b();
}

sub b {
    a();
}

a();

The best way to avoid this is don't do that. More usefully, if you have to write recursive functions try not to use multiple functions in the recursion chain, but simply the one, so you have an easier time mentally keeping track of where your calls should terminate.

As to how to detect whether something like this is happening, you would have to do something simple like increment a variable with your recursion depth and terminate (or return) if your depth exceeds a certain value. But you really shouldn't have to rely on that, it's similar to writing a while loop and using an increment there to make sure your function doesn't run out of control. Just don't recurse over a set unless you know how and when it terminates.

Another relevant question would be what are you trying to accomplish in the first place?

4
ответ дан 8 December 2019 в 17:27
поделиться

Классический разрыв двойной рекурсии - использование переменной состояния, чтобы определить, находитесь ли вы уже внутри функции:

{
    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 истинно, но умереть , возвращение или возвращение - обычное явление. Если вы используете Perl 5.10 или новее, вы можете использовать функцию состояния вместо того, чтобы вкладывать функцию в ее собственную область видимости:

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;
}
2
ответ дан 8 December 2019 в 17:27
поделиться

Я предлагаю создать подпрограмму с именем что-то вроде break_constructor_recursion (), которая использует caller () для проверки стека вызовов следующим образом:

Узнайте, какой метод в каком пакете только что вызвал меня.

Просмотрите остальную часть стека вызовов, чтобы увидеть, находится ли этот же метод в этом же пакете где-то еще выше.

Затем вы добавляете вызов break_constructor_recursion () в ваши конструкторы. Если конструктор вызывается изнутри, он взрывается.

Теперь это может привести к ложным срабатываниям; конструктор не может быть законно вызван внутри себя. Если у вас есть проблемы с этим, я бы сказал, просто найдите N дополнительных вхождений конструктора, прежде чем он обнаружит ошибку. Если есть 20 вызовов к системе :: два ::

3
ответ дан 8 December 2019 в 17:27
поделиться

использовать предупреждения;

без предупреждений:

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

Всегда всегда используйте предупреждения.

использовать предупреждения FATAL => qw (рекурсия);

#!/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.
$ 
1
ответ дан 8 December 2019 в 17:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: