web-dev-qa-db-fra.com

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

Lors d'un récent entretien d'embauche, j'ai soumis un exemple de code Perl qui utilisait le soi-disant "secret" !!opérateur . Plus tard, lors de la discussion du code, l'un des enquêteurs m'a demandé pourquoi j'avais choisi de l'utiliser et a indiqué qu'il était considéré comme une mauvaise forme. Il n'a pas expliqué pourquoi.

Mon équipe et moi utilisons cet opérateur depuis des années, sans jamais réaliser qu'il était considéré comme "de mauvaise forme".

L'opérateur "bang bang" a-t-il des effets secondaires ou d'autres comportements inattendus? Pourquoi est-il, ou pourrait-il être, considéré par certains comme une "mauvaise forme"? Existe-t-il une alternative idiomatique?

Voici quelques exemples où j'aurais considéré !! Acceptable et/ou souhaitable.

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

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

    sub foo {
        my ($input) = @_;
        my %responses = ( '' => "False", 1 => "True" );
        return $responses{ !! $input };
    }
    
  3. Utiliser 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 devez faire un transtypage typé pour une utilisation par une bibliothèque/un processus externe, comme une base de données, qui ne considère que certaines valeurs comme véridiques:

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

Votre !! Profite de deux choses obscures en Perl: les valeurs spécifiques que ! Renvoie, et que l'une des valeurs possibles est dualvar (un scalaire contenant à la fois une chaîne et un nombre). Il est vrai que l'utilisation de !! Pour aggraver ces comportements est concise. Bien que son acuité peut être un énorme avantage, il peut également être un assez gros inconvénient. N'utilisez pas de fonctionnalités qui aboutissent à un projet exigeant que "l'utilisation de Perl soit interdite sur ce projet".

Il existe de nombreuses alternatives à $count += !! (some_expression) pour compter les occurrences de valeurs véridiques de cette expression. L'un est le ternaire, $count += (some_expression) ? 1 : 0. Cela aussi est un peu obscur. Il y a une belle façon compacte de faire ce que vous voulez, qui est d'utiliser le post-if:

$x++ if some_expression;

Cela indique exactement ce que vous faites.

38
David Hammen

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

Bien qu'il existe de très belles astuces que vous pouvez utiliser en Perl, l'un des plus gros problèmes avec le langage (au moins perceptuellement) est qu'il est plutôt enclin à écrire du code une seule fois.

Après tout, pourquoi avez-vous déjà besoin de "négocier" un scalaire pour obtenir un booléen, alors que vous pouvez simplement 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 du code Perl horriblement inintelligible grâce à des opérateurs "secrets", des variables basées sur la ponctuation, etc. Et cela fonctionnera bien - par exemple - je considère toujours cela comme un chef-d'œuvre: -D Stéréogramme, source à réplication automatique

Mais ce n'est pas un "bon code". Pour une utilisation "dans le monde réel", votre code doit être clair, intelligible et facile à dépanner.

Concernant les alternatives;

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

C'est ... compter la fréquence à laquelle vous avez réussi chomp une ligne je pense? Donc, fondamentalement, il ne compte que le nombre de lignes dans votre entrée.

Pour cela, je penserais à "utiliser $.". Sauf si j'ai raté quelque chose de profond dans votre code?

"Et si ce n'est pas chomp?"

Bon ok. Que diriez-vous:

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

Pour ça:

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

J'écrirais cela comme:

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

Pour la dernière condition - empaqueter 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 - en prenant une feuille de comment Fcntl le fait en définissant des valeurs à ajouter en fonction de la position, vous n'avez donc pas à vous soucier de la position problème d'arguments:

Vous pouvez demander que les constantes flock () (LOCK_SH, LOCK_EX, LOCK_NB et LOCK_UN) soient fournies en utilisant la balise :flock.

Et puis vous pouvez:

flock ( $fh, LOCK_EX | LOCK_NB );

Ils sont définis au niveau du bit, donc ils "ajoutent" via l'opérateur or - mais cela signifie que c'est une opération idempotente, où le réglage de LOCK_NB Deux fois ne le serait pas.

Par exemple,

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

Si nous élargissons la question au moins subjectif:

Je demande les raisons d'éviter !!

  • Perl évalue la "vérité" des variables, il n'y a donc pas vraiment besoin d'un booléen logique réel.
  • Les instructions conditionnelles sont plus claires que leur algèbre booléenne équivalente. if ( $somecondition ) { $result++ } est plus clair que $result += !! $somecondition.
  • C'est sur une liste d'opérateurs secrets parce que c'est obscur. Vos futurs programmeurs de maintenance peuvent ne pas apprécier cela.
  • !!1 Est 1, !!0 Est dualvar('', 0). Bien que cela soit assez flexible, cela peut surprendre, d'autant plus que la plupart des gens ne savent même pas ce qu'est un dualvar, encore moins que ! En renvoie un. Si vous utilisez un ternaire, les valeurs que vous obtenez sont explicites: $value ? 1 : 0
65
Sobrique

Toute la question est quelque peu subjective, je voudrais donc donner un avis qui diffère des autres réponses publiées jusqu'à présent. Je ne considérerais pas la mauvaise forme de double négation en général, mais j'essaierais de l'éviter s'il existe une alternative plus lisible. Concernant vos exemples:

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

  2. Les valeurs de vérité comme clés de hachage? C'est tout simplement mauvais. Au moins, c'est surprenant pour quiconque doit maintenir votre code.

  3. Ici, l'utilisation de !! est justifié.

  4. Ici, j'utiliserais l'opérateur ternaire. Il n'est pas vraiment clair comment execute se comporte lorsqu'il passe un dualvar.

Une autre situation où il est tout à fait correct d'utiliser !! correspond à la conversion de certaines valeurs en booléens, 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 devraient connaître le !! idiome de langages typés statiquement où il est souvent utilisé comme cast pour booléen. Si c'est ce que vous voulez faire et qu'il n'y a aucun danger d'effets secondaires indésirables, optez pour la double négation.

19
nwellnhof

Peut-être que ce n'est pas si mal pour le code que vous voyez.

Cependant, votre collègue développeur pourrait le voir un jour corriger un bogue dans votre code, et il tombe dessus. Il y a deux options

  1. Il pense que vous avez fait une erreur et en supprime une !, invalider votre code
  2. Il pense que ce n'est que de la pourriture logicielle et supprime les deux !! sans une seconde pensée. Après avoir refactorisé un autre code, il se bloque soudainement ... une incompatibilité de type?

Quoi qu'il en soit, cela fera perdre du temps aux futurs développeurs, à moins qu'ils ne le connaissent. Le code intelligible n'est jamais pour vous (enfin, cela pourrait aider), mais pour tous ceux qui verront jamais votre code.

7
Sanchises

Selon le travail, c'est une mauvaise forme pour beaucoup de affaires et entretien raisons aussi ...

  1. lorsque vous travaillez sur un projet d'équipe ou tout autre code sur lequel de nombreux autres développeurs pourraient y travailler, n'obscurcissez pas (ou ne suivez pas la politique de l'entreprise)
  2. ce que les sanchises ont dit, cela entraînera plus de travail et n'affichera pas clairement les intentions du code.
  3. c'est tout simplement inutile, et comme discuté ci-dessus - il semble qu'il y ait au moins un scénario où le résultat ne fonctionnerait pas - d'où un bug.
  4. montrer les utilisations du codage obscur dans une interview, quand il existe un moyen plus simple et plus clair de le faire ne semble pas favorable dans une interview.
  5. une interview est l'endroit où vous voulez montrer que vous êtes un joueur d'équipe, pas que vous allez sortir des conneries étranges que personne d'autre ne comprend (à moins que cela ne soit la réponse à un problème non résolu bien sûr)

p.s. il aurait été judicieux de demander à l'enquêteur pourquoi il considérait cela comme "une mauvaise forme". Un conseil pratique pour le suivi de l'entretien serait de lui envoyer un remerciement et de lui demander pourquoi il considère cela comme "une mauvaise forme"

3
jon