2010-10-10 22 views
16

Perl a un opérateur conditional qui est le même un C's conditional operator.Devrais-je utiliser le conditionnel de Perl? : opérateur comme un commutateur/déclaration de cas ou au lieu de si elsif?

Pour rafraîchir, l'opérateur conditionnel en C et en Perl est:

(test) ? (if test was true) : (if test was false) 

et si elle est utilisée avec un lvalue vous pouvez assigner et test avec une seule action:

my $x= $n==0 ? "n is 0" : "n is not 0"; 

Je lisais Igor Le blog d'Ostrovsky sur A neat way to express multi-clause if statements in C-based languages et a réalisé que c'est en effet une «manière ordonnée» dans Perl.

Par exemple: (edit: utilisé sous forme plus lisible Jonathan Leffler ...)

# 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 

qui lit beaucoup plus facile que ce que beaucoup écrire en Perl: (edit: utilisé plus élégant de CJM my $t=do{ if }; forme dans la réponse de rafi)

# 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!" } 
}; 

Y a-t-il des inconvénients ou des inconvénients ici? Pourquoi ne pas écrire une forme conditionnelle étendue de cette manière plutôt que d'utiliser if(something) { this } elsif(something) { that }? L'opérateur conditionnel a . Alors:

a ? b : c ? d : e ? f : g 

est interprété comme:

a ? b : (c ? d : (e ? f : g)) 

Je suppose que vous pourriez avoir besoin entre parenthèses si vos tests utilisés l'un des rares opérateurs de priorité inférieure à ?:. Vous pouvez également mettre des blocs dans le formulaire avec des accolades je pense.

Je connais le use Switch déprécié ou sur les constructions given/when de Perl 5.10, et je ne cherche pas une suggestion pour les utiliser.

Ce sont mes questions:

  • Avez-vous vu cette syntaxe utilisée en Perl ** Je n'ai pas, et il est pas perlop ou perlsyn comme alternative à changer.

  • Existe-t-il des problèmes de syntaxe potentiels ou des «pièges» avec l'utilisation d'un opérateur conditionnel/ternaire de cette manière? Opinion: Est-il plus lisible/compréhensible pour vous? Est-ce compatible avec Idiomatic Perl?

-------- Edition -

j'ai accepté la réponse de Jonathan Leffler parce qu'il m'a pointé Perl Best Practices. La section pertinente est 6.17 sur Ternaires tabulaires. Cela m'a permis d'étudier l'utilisation plus loin. (Si vous utilisez Google Perl Ternaries, vous pouvez voir d'autres commentaires.)

deux exemples de Conway sont:

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

Mes conclusions sont les suivantes:

  • exemple Conway ?: est plus lisible et plus simple pour moi que le if/elsif forme, mais je pouvais voir comment la forme pourrait être difficile à comprendre.

  • Si vous avez Perl 5.13.1, utilisez my $t=do { given { when } }; comme une mission comme rafi has done. Je pense que given/when est le meilleur idiome maintenant, à moins que le format ternaire tableau est mieux pour votre cas particulier.

  • Si vous avez Perl 5.10+, utilisez given/when en général au lieu de Switch ou si vous avez besoin d'une sorte de commutateur de type boîtier.

  • Plus ancien Perl, c'est une bonne forme pour simple alternatives ou comme une alternative à une déclaration de cas. C'est mieux que d'utiliser Switch je pense.

  • L'associativité de droite à gauche signifie que la forme est évaluée de bas en haut. Rappelez-vous que lorsque vous utilisez ...

Répondre

15

La mise en page indiquée pour l'opérateur conditionnel est difficile à lire. Cela ressemble plus à ce que je me souviens Perl Best Practices recommander:

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... 

Et il y a des moments où il est préférable d'utiliser une notation plus compacte avec la notation if aussi:

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!" ; } 

Ces deux reformattings soulignent la similitude des différentes sections du code, ce qui le rend plus facile à lire et à comprendre.

+0

J'aime mieux votre mise en forme pour les deux alternatives, et le formulaire conditionnel que vous avez toujours me lit plus facilement que le if/elsif. Qu'est-ce que tu penses? – dawg

+0

@drewk: ils sont tous deux assez lisibles; le formulaire d'opérateur conditionnel a une seule affectation, donc il est clair qu'une seule valeur est assignée, alors qu'avec la version if-chain l'une des actions pourrait être ''$ l =" quelque chose ";'' et la faute de frappe dans le nom de la variable pourrait aller sans taches pendant un certain temps. Donc, oui, je pense qu'il y a des avantages pour l'opérateur conditionnel - mais ce n'est pas une préférence terriblement forte. –

19

J'ai vu cette expression utilisée en perl. Comme l'opérateur ternaire ? : est, bien .. un opérateur, il est documenté dans perlop, pas perlsyn. À mes yeux, il s'agit en quelque sorte de Perl idiomatique, mais le but principal de Perl est d'éviter l'absence d'une véritable instruction switch tout en n'écrivant pas d'énormes if/else -cascades. Cependant, cela a été corrigé il y a des années dans Perl 5.10.0. Ces jours-ci en utilisant le ternaire je ne vois pas beaucoup de raisons pour ne pas écrire ce qui précède celle-ci, qui semble être beaucoup plus lisible que (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!" } 
} 

ou, de Perl 5.13.1, comme :

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!" } 
    } 
}; 

Une autre alternative serait quelque chose comme ça, Wich fonctionne sur toutes les versions 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; 
} 

Bien que cela évite à la fois à l'aide d'énormes if/elsif/else -cascades, et n'a pas besoin de fonctionnalités de r Perls ecent, il ne vaut probablement pas la peine d'effort pour cet exemple simple. Cependant, je peux très bien voir une telle approche être utile avec beaucoup de conditions et avec la contrainte de vouloir supporter les vieux perls.

(Notez également que votre exemple initial ne gère pas $ n étant plus petit que zéro ou n'étant pas du tout un nombre.)

Note de CJM: Vous pouvez aussi le faire dans toutes les versions de 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!" } 
}; 
+0

Vous avez toujours été capable d'utiliser la valeur de retour de 'if' dans un bloc' do'. J'ai ajouté un exemple. Il est seulement «donné» en 5.10 et 5.12 qui ne le permet pas. – cjm

+0

Bon point. Merci! :-) – rafl

+1

Ce n'est pas * l'opérateur ternaire, c'est * un * opérateur ternaire. C'est * l'opérateur * conditionnel. – Ether

5

J'ai vu la chaîne conditionals un peu, l'utilisait parfois, et détestait toujours. C'est pratique mais laid, sauf si vous allez à l'extrême pour le formater et simplifier les expressions interstitielles.

Ils ne sont pas si difficiles à comprendre une fois que vous les avez croisés plusieurs fois et vous vous rendez compte que c'est un idiome. Il est cependant plus facile de comprendre une déclaration de switch appropriée. Il y a aussi moins de risque de perdre un côlon et de tout chambouler d'une manière difficile à repérer.