58 votes

Pourquoi "!!" considéré comme une mauvaise forme en Perl?

Lors d'un récent processus d'entrevue d'emploi, j'ai présenté quelques exemples de code Perl qui a utilisé le soi-disant "secret" !! de l'opérateur. Plus tard, lors de l'examen du code, l'un des enquêteurs m'a demandé pourquoi j'ai choisi de l'utiliser, et a indiqué qu'il était considéré comme une mauvaise forme. Il n'a pas précisé pourquoi.

Mon équipe et moi-même ont été en utilisant cet opérateur depuis des années, sans jamais se rendre compte qu'elle était considérée comme une "mauvaise forme."

Le "bang bang" de l'opérateur ont des effets de bord ou tout autre comportement inattendu? Pourquoi est-elle ou pourrait-elle être considérée comme "mauvaise forme" par certains? Est-il un idiomatiques alternative?

Ci-dessous sont quelques exemples de cas où j'aurais considéré !! acceptable et/ou souhaitable.

  1. Le code dans le codage de l'exercice, qui est un exemple d'ajout de booléens:

    while (my $line = <$F>) {
        # snip
        exists $counts{lines} and $counts{lines} += !! chomp $line;
    }
    
  2. À l'aide d'une valeur booléenne comme une clé de hachage (clairement d'un exemple simplifié):

    sub foo {
        my ($input) = @_;
        my %responses = ( '' => "False", 1 => "True" );
        return $responses{ !! $input };
    }
    
  3. À l'aide d'un booléen dans une opération au niveau du bit, ou même pack():

    sub foo {
        my ( $a, $b, $c ) = @_;
        my $result = !!$a + (!! $b)<<1 + (!! $c)<<2;
        return $result;
    }
    
  4. Vous avez besoin de faire un transtypage pour une utilisation par une bibliothèque externe/processus, comme une base de données, qui ne considère que certaines valeurs truthy:

    my $sth = $dbh->prepare("INSERT INTO table (flag,value) VALUES (?,?)")
    $sth->execute("i_haz_cheeseburger", !! $cheeseburger_string)
    

65voto

Sobrique Points 19573

Je l'appellerais "mauvaise forme", car il n'est tout simplement pas nécessaire. Vous n'avez pas besoin de booléenne convertir en Perl. Et donc, au mieux, c'est redondant, et au pire c'est un obscurcissement.

Bien qu'il existe quelques très belles astuces que vous pouvez utiliser en Perl, l'un des plus grands problèmes avec la langue (en terme de perception au moins), c'est qu'il est plutôt enclin à "écrire une fois" du code.

Après tout - pourquoi n'avez-vous jamais besoin de "double-inverser' un scalaire pour obtenir un booléen, quand vous pouvez tester le scalaire?

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

Ou:

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

Et c'est vrai - vous pouvez écrire quelques terrifiant inintelligible code Perl grâce au "secret" des opérateurs, des signes de ponctuation à base de variables, etc. Et ça marchera très bien - par exemple, je considère que c'est un chef-d'œuvre: 3-D Stéréogramme, reproductible source

Mais il n'est pas "bon code". Pour le "monde réel" l'utilisation, votre code doit être claire, compréhensible et facile à résoudre.

Concernant les solutions de remplacement;

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

C'est ... compter combien de fois vous avez réussi à chomped une ligne, je pense? Donc, fondamentalement, il seulement compte des numéros de lignes dans votre entrée.

Pour cela, je serais de la pensée " utiliser $.'. À moins que j'ai raté quelque chose d'important au sujet de votre code?

"Si ce n'est pas chomp?"

Eh bien, OK. Comment à ce sujet:

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

Pour cela:

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

J'aurais écrit que:

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

Pour la dernière condition d'emballage des drapeaux dans un octet:

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

print flags ( 1, 1, 1 );

Ou peut-être si vous faites quelque chose comme ça - prendre une feuille à partir de combien de Fcntl t-il par la définition de valeurs à ajouter basée sur la position, de sorte que vous n'avez pas à vous soucier de la position des arguments problème:

Vous pouvez demander à ce que le troupeau() constantes (LOCK_SH, LOCK_EX, LOCK_NB et LOCK_UN) être fourni par l'aide de la balise :flock.

Et puis, vous pouvez:

flock ( $fh, LOCK_EX | LOCK_NB );

Elles sont définies au niveau du bit, de sorte qu'ils 'ajouter' via l' or opérateur - mais cela signifie que c'est un idempotent de l'opération, où la définition LOCK_NB deux fois de ne pas l'être.

Par exemple,

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

Si nous élargissons la question pour le moins subjectif:

Je vous demande les raisons pour l'éviter !!

  • Perl évalue la "vérité" de variables, donc il n'y a pas vraiment besoin de beaucoup pour une réelle logique booléenne.
  • Les instructions conditionnelles sont plus claires que leur équivalent algèbre booléenne. if ( $somecondition ) { $result++ } est plus claire que l' $result += !! $somecondition.
  • C'est un secret liste des opérateurs parce qu' il est obscur. Votre futur entretien programmeurs peuvent ne pas apprécier.
  • !!1 est 1, !!0 est dualvar('', 0). Alors que c'est assez flexible, il peut surprendre, surtout depuis que la plupart des gens ne savent même pas ce qu'est un dualvar est, beaucoup moins qu' ! de retours. Si vous utilisez un ternaire, c'est explicite quelles sont les valeurs que vous obtenez:$value ? 1 : 0

39voto

David Hammen Points 17912

Votre !! profite de deux choses obscures en Perl: Les valeurs spécifiques qui ! retourne, et que l'une des valeurs possibles est dualvar (un scalaire contenant à la fois une chaîne et un nombre). À l'aide de !! de composé de ces comportements est certes lapidaire. Alors que sa pourriture molle peut être un énorme plus, il peut aussi être un gros moins. Ne pas utiliser les fonctionnalités de ce résultat dans un projet exigeant que "l'utilisation de Perl est interdit sur ce projet."

Il y a beaucoup de solutions de rechange à l' $count += !! (some_expression) pour compter les occurrences de truthy valeurs de cette expression. L'un est le ternaire, $count += (some_expression) ? 1 : 0. Ça aussi c'est un peu obscur. Il y a une belle façon compacte à faire ce que vous voulez, ce qui est d'utiliser la poste-si:

$x++ if some_expression;

Ceci dit exactement ce que vous faites.

18voto

nwellnhof Points 7740

Toute la question est quelque peu subjective, donc je voudrais donner un avis qui diffère de l'autre réponses postées jusqu'à présent. Je ne considère pas la double négation mauvais en général, mais je voudrais essayer de l'éviter, s'il y a un plus lisible alternative. Quant à vos exemples:

  1. Utilisez quelque chose comme $count++ if $condition.

  2. La vérité valeurs clés de hachage? C'est tout simplement mal. Au moins, c'est surprenant pour quelqu'un qui a à maintenir votre code.

  3. Ici l'utilisation de la !! est justifiée.

  4. Ici, je voudrais utiliser l'opérateur ternaire. Il n'est pas vraiment clair comment execute se comporte lorsqu'il est passé d'un dualvar.

Une autre situation où il est complètement OK pour utiliser !! est lors de la conversion de certaines valeurs Booléennes, par exemple dans une instruction de retour. Quelque chose comme:

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

La plupart des programmeurs doivent connaître le !! idiome de langages statiquement typés, où il est souvent utilisé comme un cast vers le type bool. Si c'est ce que vous voulez faire et il n'y a pas de danger d'effets secondaires non désirés, rendez-vous avec la double négation.

6voto

sanchises Points 457

C'est peut-être pas si mal pour le code que vous voyez.

Cependant, vos collègues développeur peut le voir un jour la correction d'un bug dans votre code, et tombe sur cette. Il y a deux options

  1. Il pense que vous avez fait une erreur et enlève un !, d'invalider votre code
  2. Il pense que c'est juste le logiciel de la pourriture, et supprime les deux !! , sans une seconde pensée. Après refactoring d'autres codes, soudain, elle se bloque sur... une incompatibilité de type?

De toute façon, il va perdre le temps de futurs développeurs, à moins qu'ils se trouvent être familier avec elle. Intelligible code n'est jamais pour soi-même (eh bien, il pourrait aider), mais pour tout les autres qui verront jamais votre code.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X