Почему «!!» считается плохой формой в Perl?

60

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

Моя команда и я используем этот оператор в течение многих лет, никогда не понимая, что это считается "плохой формой".

Имеет ли оператор "bang bang" побочные эффекты или другое неожиданное поведение? Почему это или может быть, что-то считается "плохой формой"? Есть ли идиоматическая альтернатива?

Ниже приведены несколько примеров, где я мог бы считать !! приемлемым и/или желательным.

  • Фактический код в упражнении для кодирования, который является примером добавления булевых:

    while (my $line = <$F>) {
        # snip
        exists $counts{lines} and $counts{lines} += !! chomp $line;
    }
    
  • Использование логического значения в качестве хэш-ключа (явно упрощенного примера):

    sub foo {
        my ($input) = @_;
        my %responses = ( '' => "False", 1 => "True" );
        return $responses{ !! $input };
    }
    
  • Использование логического в побитовом режиме или даже pack():

    sub foo {
        my ( $a, $b, $c ) = @_;
        my $result = !!$a + (!! $b)<<1 + (!! $c)<<2;
        return $result;
    }
    
  • Вам нужно сделать typecast для использования внешней библиотекой/процессом, например, с базой данных, которая считает только определенные значения правными:

    my $sth = $dbh->prepare("INSERT INTO table (flag,value) VALUES (?,?)")
    $sth->execute("i_haz_cheeseburger", !! $cheeseburger_string)
    
  • 4
    Это может в конечном итоге быть на основе мнения и, следовательно, ОТ. В конце концов - идиомы кода, которые вам нравятся / не нравятся, - это мнение, а стили кода - больше политики. Однако - я привел причину, которую я бы назвал «плохой формой», и подумал как совокупность возможных причин, почему это может (или не может) быть, это не так уж плохо.
  • 1
    Я думаю, что есть ответ - у вас есть (довольно грубый) пример людей, которые не могут понять, почему вы это сделаете, что, на мой взгляд, соотносится с вашим будущим программистом по обслуживанию.
Показать ещё 8 комментариев
Теги:
type-conversion
boolean

5 ответов

39
Лучший ответ

В вашем !! используются две неясные вещи в Perl: конкретные значения, возвращаемые !, и что одно из возможных значений - doublevar (скаляр, содержащий как строку, так и число). Использование !! для объединения этих поведений, по общему признанию, является проницательным. Хотя его ценность может быть огромным плюсом, она также может быть довольно большим минусом. Не используйте функции, которые приводят к тому, что проект требует, чтобы "использование Perl было запрещено в этом проекте".

Существует множество альтернатив $count += !! (some_expression) для подсчета вхождения правных значений этого выражения. Один из них - тернарный, $count += (some_expression) ? 1 : 0. Это тоже немного неясно. Существует хороший компактный способ делать то, что вы хотите, чтобы использовать post-if:

$x++ if some_expression;

Это точно говорит о том, что вы делаете.

  • 2
    Согласитесь об использовании post-if, но троичный оператор не является "неясным" и не тупым
  • 0
    @EdGriebel - Расскажите об этом всем людям, которые пишут правила, запрещающие использование троичного оператора. У меня лично с этим нет проблем, но есть целая группа людей, которые думают, что даже condition ? 0 : 1 (стандартная техника Java для преобразования логического числа в целое) является злодеянием.
68

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

Несмотря на то, что в Perl есть несколько действительно хороших трюков, одна из самых больших проблем с языком (по крайней мере, по восприятию) заключается в том, что он скорее склонен к "писать один раз".

В конце концов - зачем вам когда-либо понадобиться "двойное отрицание" скаляра, чтобы получить логическое значение, когда вы можете просто проверить скаляр?

my $state = !! 'some_string';
if ( $state ) { print "It was true\n" };

Или:

if ( 'some_string' ) { print "It was true\n" };

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

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

Относительно альтернатив;

while (my $line = <$F>) {
    # snip
    exists $counts{lines} and $counts{lines} += !! chomp $line;
}

Это... подсчет, как часто вы успешно chomp редактировали строку, я думаю? Таким образом, в основном он просто подсчитывает количество строк на вашем входе.

Для этого я бы подумал "используйте $.". Разве я не пропустил что-то глубокое о вашем коде?

"Что делать, если он не горит?"

Хорошо, хорошо. Как насчет:

 $counts{lines}++ if foo();

Для этого:

sub foo {
    my ($input) = @_;
    my %responses = ( "" => "False", 1 => "True" );
    return $responses{ !! $input };
}

Я бы написал это как:

sub foo {
    my ($input) = @_;
    return $input ? "True" : "False";
}

Для последнего условия - упаковка флагов в байт:

sub flags {
    my @boolean_flags = @_;
    my $flags = 0;
    for ( @boolean_flags ) {
        $flags<<=1;
        $flags++ if $_;
    }
    return $flags;
}

print flags ( 1, 1, 1 );

Или, может быть, если вы делаете что-то вроде этого - беря лист от того, как Fcntl делает это, определяя значения для добавления на основе позиции, так что вы не беспокоясь о проблеме позиционных аргументов:

Вы можете запросить, чтобы константы flock() (LOCK_SH, LOCK_EX, LOCK_NB и LOCK_UN) были предоставлены с помощью тега :flock.

И затем вы можете:

flock ( $fh, LOCK_EX | LOCK_NB );

Они определены поразрядным образом, поэтому они "добавляют" через оператор or, но это означает, что это идемпотентная операция, где установка LOCK_NB дважды не будет.

Например,

 LOCK_UN = 8
 LOCK_NB = 4
 LOCK_EX = 2
 LOCK_SH = 1

Если мы разберем вопрос на менее субъективный:

Я прошу причины, чтобы избежать!

  • Perl оценивает "правду" переменных, поэтому на самом деле нет необходимости в фактическом логическом логическом выражении.
  • Условные утверждения яснее их эквивалентной булевой алгебры. if ( $somecondition ) { $result++ } понятнее, чем $result += !! $somecondition.
  • Это в секретном списке операторов, потому что это неясно. Ваши будущие программисты по обслуживанию могут не оценить это.
  • !!1 - 1, !!0 - dualvar('', 0). Хотя это довольно гибко, это может удивить, тем более, что большинство людей даже не знают, что такое dualvar, тем более что ! возвращает один. Если вы используете тернар, то явные значения, которые вы получаете: $value ? 1 : 0
  • 9
    Я должен сказать - я не могу думать ни о каких сценариях, где мне нужен «настоящий логический». Оценка истинности скаляра всегда делала свое дело. Случай с нечетным краем в трех состояниях true / false / undef является, пожалуй, единственным исключением, и там вы defined и // .
  • 0
    ХОРОШО. Извините, вам понадобится расширить «добавление двух логических значений». Я имею в виду, я видел его в контексте логического or операции. (Как это делает C / C ++). Но у вас уже есть логическое значение or|| для логического и | для побитового) (или xor для логического или ^ для побитового)
Показать ещё 6 комментариев
19

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

  • Используйте что-то вроде $count++ if $condition.

  • Значения истины как хеш-ключи? Это просто зло. По крайней мере, это удивительно для всех, кто должен поддерживать ваш код.

  • Здесь использование !! оправдано.

  • Здесь я бы использовал тернарный оператор. На самом деле не совсем ясно, как execute ведет себя при передаче двоичного файла.

Другая ситуация, когда полностью использовать !!, - это приведение определенных значений в Boolean, например, в оператор return. Что-то вроде:

# Returns true or false.
sub has_item {
    my $self = shift;
    return !!$self->{blessed_ref};
}

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

  • 0
    Ах, да, я забыл об обратном случае, о котором вы упомянули в конце. Это еще один, который я использовал раньше.
  • 1
    В последнем случае я хотел бы просто использовать «определенные», чтобы вас не сбивали с толку настоящие, а ложные значения.
Показать ещё 4 комментария
7

Возможно, это не так плохо для кода, который вы видите только.

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

  • Он думает, что вы допустили ошибку и удалил один !, недействительный код
  • Он думает, что это всего лишь программная гниль, и удаляет обе !! без второй мысли. После рефакторинга какого-то другого кода он внезапно зависает... несоответствие типа?

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

  • 2
    В студенческие годы я давал своему другу тяжелое время для того, чтобы поместить двухуровневых трехуровневых операторов в одну линию и назвал их «компактными и крутыми». Я сказал: «Это глупо и невозможно поддерживать, потому что мне нужно потратить 30 минут, чтобы что-то отследить вручную».
  • 2
    Как однажды сказал мудрец. Отладка в два раза сложнее, чем кодирование. Если вы будете настолько умны, насколько это возможно, когда будете писать, как вы будете когда-либо отлаживать его?
3

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

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

p.s. было бы в хорошей форме спросить интервьюера, почему он считал его "плохим". Удобный совет для интервью, будет послать ему благодарность и спросить его, почему он считает это "плохой формой"

  • 0
    +1 за продолжение, чтобы показать интервьюеру, что вы можете разумно обсудить это и уважать их мнение.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню