2009-11-12 3 views
3

Je travaille en Perl et j'ai rencontré un résultat impair en utilisant l'opérateur conditionnel.Comportement de l'opérateur conditionnel Odd Perl

Le code en question:

($foo eq "blah") ? @x = @somearray : @y = ("another","array"); 

Essayer de compiler ce code entraîne l'erreur "Assignment to both a list and a scalar at XXX line YY, near ');'". En essayant de localiser la source de l'erreur, j'ai écrit ceci en utilisant deux manières différentes de représenter un tableau en Perl et ils reviennent tous avec la même erreur. Maintenant, au début, je pensais que c'était juste une erreur évidente stupide avec les instructions d'affectation, mais juste pour satisfaire ma curiosité, je réécris la déclaration d'une manière plus détaillée:

if($foo eq "blah") { 
    @x = @somearray; 
} else { 
    @y = ("another","array"); 
} 

Cette version du code compilé parfaitement bien.

Existe-t-il une fine distinction entre le fonctionnement de l'opérateur conditionnel et une instruction if-else de base qui me manque ici? J'ai toujours compris que l'opérateur conditionnel n'était qu'une version abrégée de la deuxième déclaration. S'il n'y a pas de différence fonctionnelle entre les deux, pourquoi Perl s'opposerait-il à la première déclaration, mais pas à la seconde?

+4

Personnellement, je décrirais le deuxième code échantillon comme "plus clair" et "plus simple" plutôt que "verbeux". – qid

+0

s/ternaire/conditionnel/!!! Il pourrait y avoir plus d'un opérateur ternaire. – Ether

+0

Excuses, je n'ai certainement aucun problème avec la deuxième version et je ne jugerais pas une personne pour l'utiliser! Peut-être que «plus explicite» serait une meilleure façon de le dire, même si je ne voulais pas dire verbeux pour avoir une connotation négative. – warhorus

Répondre

15
 
$ perl -MO=Deparse -e'($foo eq "blah") ? @x = @somearray : @y = ("another","array");' 
Assignment to both a list and a scalar at -e line 1, near ");" 
-e had compilation errors. 
$foo eq 'blah' ? (@x = @somearray) : @y = ('another', 'array'); 
$ perl -MO=Deparse -e'($foo eq "blah") ? @x = @somearray : (@y = ("another","array"));' 
$foo eq 'blah' ? (@x = @somearray) : (@y = ('another', 'array')); 
-e syntax OK 

Notez les parenthèses: ?: lie plus serré que =.

+0

Merci, a travaillé comme un charme! – warhorus

+6

Bien que ce soit pourquoi Perl n'aime pas le code, je dirais que la véritable racine de l'erreur est l'abus de l'opérateur '?:'. –

7

La documentation perlop indique clairement que vous devez mettre des parenthèses autour des opérateurs d'affectation.

Ne pas utiliser de parenthèses est une tige pour votre propre dos si vous ne comprenez pas la priorité de l'opérateur. Arrêtez d'essayer d'être trop intelligent pour votre propre bien!

+1

"essayer d'être trop intelligent pour votre propre bien" - c'est une bonne définition d'un programmeur :) – Ether

+3

J'ai eu des arguments chauffés avec des "programmeurs" qui déconseillent l'utilisation de parenthèses. C'est bien si vous gardez une table d'opérateurs préséance dans votre tête et ne changez pas de langues régulièrement. Je programme depuis plus de 19 ans et je change assez souvent de langue pour ne pas faire d'hypothèses sur le langage sous-jacent. De plus, cela signifie que je peux porter des expressions avec moins de risques. –

+0

Je me suis délibérément abstenu de mémoriser les tables de préséance C et C++. Je me dis que je ferai moins de mal si je ne sais pas où vont les parens. –

9

L'opérateur conditionnel Perl est censé être

variable $ = (expression)? affectation réelle: affectation erronée; Ce que vous faites ressemble à ce qu'il devrait fonctionner et est fondamentalement le même que l'instruction if/else. Mais est juste assez différent de la norme pour avoir des problèmes.

+0

En C et Perl, il est parfaitement possible d'ignorer le résultat d'une expression. –

+1

@PP Oui, mais pas à la pauvre sève qui le lit. – Schwern

2

Ce serait un bon endroit pour utiliser les opérateurs de priorité inférieure 'et' et 'ou'.

$foo eq 'blah' and @x = @somearray or @y = ('another', 'array'); 

si vous êtes sûr que @x = @somearray sera toujours vrai. ou vous pouvez les retourner.

+0

Au nom d'une tromperie excessive, '$ foo eq 'blah' et (undef, @x) = (undef, @somearray) ou @y = ('autre', 'array');' fonctionnera même si '@ somearray == 0': D – ephemient

+1

Je ne suis pas d'accord, ce serait une bonne utilisation de 'if() {else {}'. Tout le reste est difficile à lire et excessivement intelligent. La confusion évidente dans les autres commentaires fournit une vérification indépendante de la difficulté inhérente à la surutilisation et à l'abus de l'opérateur de court-circuit 'et'. – daotoad

+0

[commentaire précédent supprimé pour être juste faux] – mob

6

Ceci est un peu orthogonale à votre question, mais il porte indiquant: opérateur conditionnel de Perl se propage le contexte du premier argument vers le bas dans les arguments deuxième ou troisième, donc cela vous donne des résultats non désirés:

$x = ($foo eq "blah") ? $somevalue : ("another","array"); 

Si le conditionnel était faux, $x recevrait à la place une seule valeur entière 2 (le nombre d'éléments dans le troisième argument).

Si d'autre part vous tentiez d'effectuer une mission purement scalaire:

# this is wrong, for the same order-of-operations reasons as with arrays 
($foo eq "blah") ? $x = $somevalue : $x = "another value"; 

Ce serait une façon raisonnable (et le meilleur) pour résoudre la situation:

$x = ($foo eq "blah") ? $somevalue : "another value"; 

De même , vous pouvez optimiser votre code d'origine de cette façon:

@x = ($foo eq "blah") ? @somearray : ("another","array"); 
+0

Orthogonal ou pas, c'est un bon point à garder à l'esprit tout en traitant avec l'opérateur?:! – warhorus

+0

... sauf que le code original est absurdement délicat et que l'on affecte '@ x' dans un cas et' @ y' dans l'autre cas. – ephemient

+0

'($ foo eq" bla ")? @x: @y = ($ foo eq "blah")? @searearray: qa (un autre tableau); 'se comportera comme le code original. – ephemient

Questions connexes