Должен ли я использовать условные Perl? : оператор как оператор switch / case или вместо if elsif? - PullRequest
17 голосов
/ 10 октября 2010

Perl имеет условный оператор , который является тем же условным оператором C .

Чтобы обновить, условный оператор в C и в Perl:

(test) ? (if test was true) : (if test was false)

и, если используется с lvalue, вы можете назначить и протестировать одним действием:

my $x=  $n==0 ? "n is 0" : "n is not 0";

Я читал блог Игоря Островского на Прекрасный способ выразить несколько предложений, если операторы на языках C и понял, что это действительно "аккуратный путь" и в Perl.

Например: (редактировать: используется более читабельная форма Джонатана Леффлера ...)

# ternary conditional form of if / elsif construct:
my $s=
      $n == 0     ? "$n ain't squawt"
    : $n == 1     ? "$n is not a lot"
    : $n < 100    ? "$n is more than 1..."
    : $n < 1000   ? "$n is in triple digits"
    :               "Wow! $n is thousands!" ;  #default

Который читает намного проще, чем многие пишут на Perl: (редактировать: в ответе Рафи использовалась более элегантная форма my $t=do{ if }; от cjm)

# Perl form, not using Switch or given / when
my $t = do {
    if    ($n == 0)   { "$n ain't squawt"        }
    elsif ($n == 1)   { "$n is not a lot"        }
    elsif ($n < 100)  { "$n is more than 1..."   }
    elsif ($n < 1000) { "$n is in triple digits" }
    else              {  "Wow! $n is thousands!" }
};

Есть здесь какие-нибудь ошибки или недостатки? Почему бы мне не написать расширенную условную форму таким образом, а не использовать if(something) { this } elsif(something) { that }?

Условный оператор имеет правильную ассоциативность и низкий приоритет . Итак:

a ? b : c ? d : e ? f : g

интерпретируется как:

a ? b : (c ? d : (e ? f : g))

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

Я знаю о устаревших use Switch или о given/when конструкциях Perl 5.10, и я не ищу предложений по их использованию.

Вот мои вопросы:

  • Вы видели этот синтаксис, используемый в Perl? ** У меня нет, и его нет в perlop или perlsyn в качестве альтернативы для переключения.

  • Существуют ли потенциальные синтаксические проблемы или «ошибки» при использовании условного / троичного оператора таким образом?

  • Мнение: это более читабельно / понятно для вас? Это согласуется с Idiomatic Perl?

-------- Редактировать -

Я принял ответ Джонатана Леффлера, потому что он указал мне на Perl Best Practices . Соответствующий раздел - 6,17 на Табличные тройки. Это позволило мне продолжить исследование использования. (Если вы используете Google Perl Tabular Ternaries, вы можете увидеть другие комментарии.)

Два примера Конвея:

my $salute;
if ($name eq $EMPTY_STR) {
    $salute = 'Dear Customer';
}
elsif ($name =~ m/\A ((?:Sir|Dame) \s+ \S+)/xms) {
    $salute = "Dear $1";
}

elsif ($name =~ m/([^\n]*), \s+ Ph[.]?D \z/xms) {
    $sa1ute = "Dear Dr $1";
}
else {
    $salute = "Dear $name";
}

VS:

           # Name format...                            # Salutation...
my $salute = $name eq $EMPTY_STR                       ? 'Dear Customer'
           : $name =~ m/ \A((?:Sir|Dame) \s+ \S+) /xms ? "Dear $1"
           : $name =~ m/ (.*), \s+ Ph[.]?D \z     /xms ? "Dear Dr $1"
           :                                             "Dear $name"
           ;

Мои выводы:

  • Пример ?: Конвея для меня более читабелен и проще, чем форма if/elsif, но я мог видеть, как форма может стать трудной для понимания.

  • Если у вас есть Perl 5.13.1, используйте my $t=do { given { when } }; в качестве назначения, как Рафи. Я думаю, given/when - лучшая идиома сейчас, если только табличный троичный формат не лучше для вашего конкретного случая.

  • Если у вас есть Perl 5.10+, используйте given/when в целом вместо Switch или если вам нужен какой-то переключатель типа регистра.

  • Старые Perl, это прекрасная форма для простых альтернатив или в качестве альтернативы инструкции case. Я думаю, это лучше, чем использовать Switch.

  • Ассоциативность справа налево означает, что форма оценивается снизу вверх. Помните, что при использовании ...

Ответы [ 4 ]

19 голосов
/ 10 октября 2010

Я видел эту идиому, используемую в Perl.Поскольку троичный оператор ? : - это, ну ... оператор, он задокументирован в perlop, а не perlsyn.

На мой взгляд, это своего рода идиоматический Perl,но основная цель этого в Perl, похоже, состоит в том, чтобы избежать отсутствия правильного switch оператора, в то время как не пишутся огромные if/else -каскады.Однако это было исправлено много лет назад в perl 5.10.0.В настоящее время я не вижу многих причин, чтобы не писать вышеприведенное, как это, которое представляется гораздо более читабельным, чем (ab) с использованием троичного:

given ($n) {
    when (0)         { $t = "$_ ain't squawt"        }
    when (1)         { $t = "$_ is not a lot"        }
    when ($_ < 100)  { $t = "$_ is more than 1..."   }
    when ($_ < 1000) { $t = "$_ is in triple digits" }
    default          { $t = "Wow! $_ is thousands!"  }
}

или, по состоянию на perl 5.13.1,даже как:

my $t = do {
    given ($n) {
        when (0)         { "$_ ain't squawt"        }
        when (1)         { "$_ is not a lot"        }
        when ($_ < 100)  { "$_ is more than 1..."   }
        when ($_ < 1000) { "$_ is in triple digits" }
        default          {  "Wow! $_ is thousands!" }
    }
};

Другая альтернатива была бы примерно такой, которая работает на всех версиях Perl:

my @cases = (
    [sub { $_ == 0 },   sub { "$_ ain't squawt"        }],
    [sub { $_ == 1 },   sub { "$_ is not a lot"        }],
    [sub { $_ < 100 },  sub { "$_ is more than 1..."   }],
    [sub { $_ < 1000 }, sub { "$_ is in triple digits" }],
    [sub { 1 },         sub { "Wow! $_ is thousands!"  }],
);

for my $case (@cases) {
    local $_ = $n;
    next unless $case->[0]->();
    $t = $case->[1]->();
    last;
}

Хотя это позволяет избежать использования огромных if/elsif/else -каскадов и не используетНе нужны особенности последних Perls, вероятно, не стоит усилий для этого простого примера.Тем не менее, я очень вижу, что подобный подход полезен с большим количеством условий и ограничением желания поддерживать старые perls.

(Также обратите внимание, что ваш первоначальный пример не обрабатывает $ n, будучи меньше, чемноль или вовсе не число.)

Примечание от cjm : Вы также можете сделать это во всех версиях Perl 5:

my $t = do {
    if    ($n == 0)   { "$n ain't squawt"        }
    elsif ($n == 1)   { "$n is not a lot"        }
    elsif ($n < 100)  { "$n is more than 1..."   }
    elsif ($n < 1000) { "$n is in triple digits" }
    else              {  "Wow! $n is thousands!" }
};
15 голосов
/ 10 октября 2010

Схема, показанная для условного оператора, плохо читается. Это больше похоже на то, что я помню Perl Best Practices рекомендация:

my $s = $n == 0   ? "$n ain't squawt"
      : $n == 1   ? "$n is not a lot"
      : $n < 100  ? "$n is more than 1..."
      : $n < 1000 ? "$n is in triple digits"
      :             "Wow! $n is thousands!";  # default...

И бывают случаи, когда лучше использовать более компактную нотацию с нотацией if:

  if    ($n == 0)   { $t = "$n ain't squawt";        }
  elsif ($n == 1)   { $t = "$n is not a lot";        }
  elsif ($n < 100)  { $t = "$n is more than 1...";   }
  elsif ($n < 1000) { $t = "$n is in triple digits"; }
  else              { $t = "Wow! $n is thousands!" ; }  

Оба эти переформатирования подчеркивают сходство различных разделов кода, облегчая чтение и понимание.

5 голосов
/ 11 октября 2010

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

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

4 голосов
/ 11 октября 2010

И еще один способ!

my $t = sub {
    return "$n ain't squawt"        if $n == 0;
    return "$n is not a lot"        if $n == 1;
    return "$n is more than 1..."   if $n < 100;
    return "$n is in triple digits" if $n < 1000;
    return "Wow! $n is thousands!";
}->();

Я коснусь этого в нескольких сообщениях в блоге, которые я сделал:

/ I3az /

...