Следует ли избегать вложенных операторов if в Perl?

Я делаю некоторые Perl и вижу, что мои вложенные утверждения "if" сводят меня с ума. Мне удалось уменьшить некоторые из них с помощью защитных блоков в другой секции, но я застрял здесь.

Как вы думаете, я могу оставить код как есть, или есть "правильный" способ рефакторинга следующего ? (Я также признаю, что относительно новичок в Perl)

это фактически подпрограмма, запрашивающая ввод пользователя для каждого параметра списка (внешнего файла). $[3] - соответствующий шаблон, $[2] является значением по умолчанию для рассматриваемого параметра (NULL, если нет), $_[1] указывает, является ли он обязательным или нет. оператор 'next' ссылается на следующий параметр read (while loop).

С каждого (спасибо !), вот новейшая версия.

100         if ( $input ne '' && ( $input !~ $match || $input =~ /'.+'/ ) ) {
101             print "! Format not respected. Match : /$match/ (without ' ')n";
102             next;
103         }
104         if ( $input eq '' ) {
105             if ( $default eq 'NULL' ) {
106                 if ( $manda eq 'y' ) {
107                     print "! Mandatory parameter not filled inn";
108                     next;
109                 }
110                 print "+ Ignoring parameter.n";
111                 $input = '';
112             }
113             else {
114                 print "+ Using default valuen";
115                 $input = $default;
116             }
117         }

 98        if($input eq ''){
 99             if($_[2] eq 'NULL'){
100                 if($_[1] eq 'y'){
101                     print "! Mandatory parameter not filled inn";
102                     next;
103                 }
104                 else{
105                     print "+ Ignoring parameter.n";
106                     $input = '';
107                 }
108             }
109             else{
110                 print "+ Using default valuen";
111                 $input = $_[2];
112             }
113         }
114         elsif($input !~ $_[3] || $input =~ /'.+'/){
115                 print "! Format not respected. Match : /$_[3]/ (without ' ')n"; 
116                 next;
117             }
118         }

10 ответов


вот немного более читаемая версия chaos ' answer:

# Set sane variable names...
my ($is_required, $default, $pattern) = @_

# Convert the external string convention for simpler evaluation...
$default = undef if $default eq 'NULL'

# Refuse problematic input before going any further...
if ($input ne '' && $input !~ $pattern || $input =~ /'.+'/) {
    print "! Format not respected. Match : /$pattern/ (without \' \')\n"; 
    next;
}


# If there was no input string...
if($input eq '') {

    # Set the default, if one was provided...
    if( $default ) {
        print "+ Using default value\n";
        $input = $default;
    } 
    # otherwise, complain if a default was required...
    else {
        if( $is_required eq 'y' ){
            print "! Mandatory parameter not filled in\n";
            next;
        }
        print "+ Ignoring parameter (no input or default provided).\n";
    }
}

ключевые требования:

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

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

$format_not_respected = sub {
    return 0 if ...;
    print "! Format not respected....";
    return 1;
}
$missing_mandatory_param = sub {
    return 0 if ...;
    print "! Mandatory parameter not filled in\n";
    return 1;
}

next if $format_not_respected->();
next if $missing_mandatory_param->();
etc...

основная забота состоит в том, чтобы сохранить код читаемым.

Если вы можете получить читаемый код с вложенными операторами if, продолжайте. Но всегда сохраняйте здравый смысл.


if($input ne '' && ($input !~ $_[3] || $input =~ /'.+'/)) {
    print "! Format not respected. Match : /$_[3]/ (without \' \')\n"; 
    next;
}
if($input eq '') {
    if($_[2] eq 'NULL') {
        if($_[1] eq 'y'){
            print "! Mandatory parameter not filled in\n";
            next;
        }
        print "+ Ignoring parameter.\n";
        $input = '';
    } else {
        print "+ Using default value\n";
        $input = $_[2];
    }
}

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

use constant MANDATORY => 1,
             DEFAULT => 2,
             PATTERN => 3;
...
if($_[DEFAULT] eq 'NULL') {
   ...
}

что касается вложенности-вы должны часто пытаться уменьшить отступ (что означает сохранение низкого уровня вложенности), но никогда не делайте этого за счет сохранения кода понятным. У меня нет проблем с уровнем вложенности, но это тоже лишь небольшой отрезок кода. Если это действительно проблема, вы можете разбить условия на отдельные подпрограммы.


если логика требует вложенного оператора if, то я думаю, что с ними нет ничего плохого.

тем не менее, вы можете улучшить читаемость вашего кода с помощью

  1. используя только немного больше белого пространства и
  2. используя свои собственные переменные вместо работы непосредственно на @_

разве это не более читабельно?

 98        if ($input eq '') {
 99             if ($default eq 'NULL'){
100                 if ($input eq 'y'){
101                     print "! Mandatory parameter not filled in\n";
102                     next;
103                 }
104                 else {
105                     print "+ Ignoring parameter.\n";
106                     $input = '';
107                 }
108             }
109             else {
110                 print "+ Using default value\n";
111                 $input = $default;
112             }
113         }
114         elsif ($input !~ $foo || $input =~ /'.+'/) {
115                 print "! Format not respected. Match : /$foo/ (without \' \')\n"; 
116                 next;
117             }
118         }

учитывая, что вы, вероятно, будете делать спагетти goto в противном случае, абсолютно нет.

что может быть лучше корпус переключателя.


вы можете упростить до следующего, если вам не нравятся все остальные.

if($input eq ''){
    $input = $_[2];
    $output = "+ Using default value\n";
    if($_[2] eq 'NULL'){
        $input = '';
        $output = "+ Ignoring parameter.\n";
        if($_[1] eq 'y'){
            $output = "! Mandatory parameter not filled in\n";
        }
    }
}
elsif($input !~ $_[3] || $input =~ /'.+'/){
    $output = "! Format not respected. Match : /$_[3]/ (without \' \')\n"; 
}
print $output;

Я думаю, что главная (если не единственная) причина для беспокойства regardong вложенности и сложности алгоритма. В других случаях вы должны беспокоиться о читаемости и ремонтопригодности, которые могут быть адресованы с комментариями и отступами propoer.

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


Я полагаю, вы могли бы сделать логические комбинации, чтобы сгладить его:

if(($input eq '')&&($_[2] eq 'NULL')&&($_[1] eq 'y')){
  print "! Mandatory parameter not filled in\n";
  next;
}

elsif(($input eq '')&&($_[2] eq 'NULL')){
  print "+ Ignoring parameter.\n";
  $input = '';
}

elsif($input eq ''){
  print "+ Using default value\n";
  $input = $_[2];
  next;
}


elsif($input !~ $_[3] || $input =~ /'.+'/){
  print "! Format not respected. Match : /$_[3]/ (without \' \')\n";
}

print $output;

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