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

В 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";

Я читал блог Игоря Островского на Изящный способ выражения операторов if с несколькими предложениями в языках на основе 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"
           ;

Мои выводы:


person dawg    schedule 10.10.2010    source источник


Ответы (4)


Макет, показанный для условного оператора, трудно читать. Это больше похоже на то, что, как я помню, рекомендовал 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!" ; }  

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

person Jonathan Leffler    schedule 10.10.2010
comment
Мне больше нравится ваше форматирование для обеих альтернатив, и условная форма, которую вы имеете, по-прежнему читается для меня легче, чем if / elsif. Что вы думаете? - person dawg; 10.10.2010
comment
@drewk: они оба довольно читабельны; форма условного оператора имеет одно присваивание, поэтому ясно, что присваивается одно значение, тогда как в версии с цепочкой if одним из действий может быть '$l = "something";', а опечатка в имени переменной может некоторое время оставаться незамеченной. Итак, да, я думаю, что у условного оператора есть преимущества, но это не слишком сильное предпочтение. - person Jonathan Leffler; 11.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-каскадов и не требует функций последних версий Perl, вероятно, это не стоит усилий для этого простого примера. Тем не менее, я очень хорошо вижу, что такой подход полезен при многих условиях и с ограничением, связанным с желанием поддерживать старые 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!" }
};
person rafl    schedule 10.10.2010
comment
Вы всегда могли использовать возвращаемое значение if в блоке do. Я добавил пример. Только given в 5.10 и 5.12 этого не позволяет. - person cjm; 10.10.2010
comment
Это не тернарный оператор, это тернарный оператор a. Это условный оператор. - person Ether; 10.10.2010
comment
О, вы абсолютно правы. Наверное, я перепутал эти имена, потому что ?: — единственный троичный оператор в Perl (не так ли?). Отредактировано соответственно. Спасибо! - person rafl; 10.10.2010
comment
Форма test ? true : false однозначно называется условным оператором в C, C++, C#, Java, Javascript, Perl и PHP. В vb.net это называется «тройное если» или iif(). Python называет это Conditional expressions (sometimes called a “ternary operator”). На мой взгляд, его классификация — это тернарный оператор с именем the conditional operator. - person dawg; 10.10.2010
comment
@rafi: Как указано в моем посте, я знаю о конструкции given/when в Perl 5.10 и более поздних версиях, и, пожалуйста, пока отложите ее. Вы называете форму, которую я использовал, как «(ab) использование тройки». Почему это использование злоупотребляет условным оператором? Когда вы смотрите на пост Джонатана Леффлера, вы думаете, что эту форму трудно прочитать? - person dawg; 10.10.2010
comment
Да, я действительно думаю, что его труднее читать, чем многие альтернативы, и я также считаю, что его труднее писать. Вы используете ?: и фактически превращаете его в очень глубоко вложенную управляющую структуру. С некоторым умным форматированием это не так плохо, как могло бы быть, но, по крайней мере, мой мозг лучше работает с более плоскими структурами. Последняя альтернатива, которую я дал, напоминает форму cond в Лиспе, работает лучше для меня, а также лучше масштабируется для случаев, которые не так тривиальны, как ваш пример. Это очень похоже на использование хеша в качестве справочной таблицы, что вы также часто делаете, чтобы избежать if/else-каскадов. - person rafl; 10.10.2010

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

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

person brian d foy    schedule 11.10.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/

person draegtun    schedule 11.10.2010