Как я могу чисто обработать проверку ошибок в Perl? - PullRequest
1 голос
/ 03 августа 2009

У меня есть процедура Perl, которая управляет проверкой ошибок. Существует около 10 различных проверок, и некоторые из них являются вложенными, основываясь на предыдущем успехе. Обычно это не исключительные случаи, когда мне нужно было бы croak / die. Кроме того, как только возникает ошибка, нет смысла проходить остальные проверки.

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

sub lots_of_checks
{

 if(failcond)
 {
  goto failstate:
 }
 elsif(failcond2)
 {
  goto failstate;
 }

 #This continues on and on until...

 return 1; #O happy day!

 failstate:

 return 0; #Dead...
}

То, что я предпочел бы сделать, было бы примерно так:

do
{
 if(failcond)
 {
  last;
 }
 #...
};

Ответы [ 6 ]

12 голосов
/ 03 августа 2009

Пустой оператор возврата - лучший способ вернуть false из подпрограммы Perl, чем 0. Последнее значение будет действительно истинно в контексте списка:

sub lots_of_checks {
    return if fail_condition_1;
    return if fail_condition_2;
    # ...
    return 1;
}
8 голосов
/ 03 августа 2009

Возможно, вы захотите взглянуть на следующие статьи об обработке исключений в perl5:

5 голосов
/ 04 августа 2009

Почему бы вам не использовать исключения? Любой случай, когда нормальный поток кода не должен соблюдаться, является исключением. Использование «return» или «goto» - это одно и то же, просто «не то, что вы хотите».

(То, что вы действительно хотите, это продолжения, которые «return», «goto», «last» и «throw» являются особыми случаями. Хотя Perl не имеет полных продолжений, у нас есть escape-продолжения; см. http://metacpan.org/pod/Continuation::Escape)

В своем примере кода вы пишете:

do
{
 if(failcond)
 {
  last;
 }
 #...
};

Это, вероятно, так же, как:

eval {
   if(failcond){
       die 'failcond';
   }
}

Если вы хотите быть хитрым и игнорировать другие исключения:

my $magic = [];
eval {
    if(failcond){
        die $magic;
    }
}
if ($@ != $magic) {
    die; # rethrow
}

Или вы можете использовать модуль Continuation :: Escape, упомянутый выше. Но нет причин игнорировать исключения; это вполне приемлемо использовать их таким образом.

5 голосов
/ 03 августа 2009

Вы абсолютно можете делать то, что предпочитаете.

Check: {
    last Check
        if failcond1;
    last Check
        if failcond2;
    success();
}
4 голосов
/ 03 августа 2009

Учитывая ваш пример, я бы написал так:

sub lots_of_checks {
    local $_ = shift;  # You can use 'my' here in 5.10+

    return if /condition1/;
    return if /condition2/;
    # etc.

    return 1;
}

Обратите внимание на голые return вместо return 0. Это обычно лучше, потому что это уважает контекст; значение будет undef в скалярном контексте и () (пустой список) в контексте списка.

Если вы хотите удерживать точку с одним выходом (что немного неверно), вы можете сделать это, не прибегая к goto. В документации для last указано:

... сам по себе блок семантически идентичен циклу, который выполняется один раз. Таким образом, «последний» может быть использован для раннего выхода из такого блока.

sub lots_of_checks {
    local $_ = shift;
    my $all_clear;

    {
        last if /condition1/;
        last if /condition2/;
        # ...
        $all_clear = 1; # only set if all checks pass
    }

    return unless $all_clear;
    return 1;
}
2 голосов
/ 04 августа 2009

Если вы хотите сохранить единую структуру ввода / вывода, вы можете слегка изменить другие предложения, чтобы получить:

sub lots_of_checks
{
    goto failstate if failcond1;

    goto failstate if failcond2;

    # This continues on and on until...

    return 1; # O happy day!

    failstate:

    # Any clean up code here.

    return; # Dead...
}

IMO, использование в Perl формы модификатора оператора "return if EXPR" делает охранные предложения более читабельными, чем в C. Когда вы впервые видите строку, вы знаете, что у вас есть защитное предложение. Эта функция часто клеветает, но в этом случае я ее очень люблю.

Использование goto с модификатором оператора сохраняет ясность и уменьшает беспорядок, сохраняя при этом ваш стиль кода единого выхода. Я использовал эту форму, когда мне пришлось выполнить сложную очистку после неудачной проверки для подпрограммы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...