2010-10-10 12 views
16

Perl ha un operatore conditional uguale a C's conditional operator.Devo usare il condizionale di Perl? : operatore come un'istruzione switch/case o invece se elsif?

Per aggiornare, l'operatore condizionale in C e in Perl è:

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

e se utilizzato con un lvalue è possibile assegnare e test con una sola azione:

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

stavo leggendo Igor Il blog di Ostrovsky su A neat way to express multi-clause if statements in C-based languages e ha capito che questo è davvero un "modo pulito" anche in Perl.

Ad esempio: (edit: utilizzato forma più leggibile di 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 

Quale legge molto più facile di quello che molti avrebbe scritto in Perl: (edit: più elegante my $t=do{ if }; modulo utilizzato di CJM nella risposta di 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!" } 
}; 

Ci sono trucchi o svantaggi qui? Perché non dovrei scrivere una forma condizionale estesa in questo modo piuttosto che usare if(something) { this } elsif(something) { that }?

L'operatore condizionale ha right associativity and low precedence. Quindi:

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

viene interpretato come:

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

suppongo che potrebbe essere necessario se la parentesi i test usati uno dei pochi operatori di precedenza inferiore rispetto ?:. Potresti anche mettere dei blocchi nella forma con le parentesi graffe che penso.

Sono a conoscenza del deprecato use Switch o dei costrutti di Perl 5.10 given/when e non sto cercando un suggerimento per utilizzarli.

Queste sono le mie domande:?

  • hai visto questa sintassi utilizzati in Perl ** non ho, e non è in perlop o perlsyn come un supplente per passare.

  • Ci sono potenziali problemi di sintassi o 'trucchi' con l'utilizzo di un operatore condizionale/ternario in questo modo?

  • Opinione: Per te è più leggibile/comprensibile? È coerente con Idiomatic Perl?

-------- Edit -

ho accettato la risposta di Jonathan Leffler perché lui mi indicò Perl Best Practices. La sezione pertinente è 6.17 su Tonnari tabulari . Questo mi ha permesso di indagare ulteriormente sull'uso. (Se tu tollari tabulari di Google Perl, puoi vedere altri commenti.)

di Conway due esempi sono:

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

Le mie conclusioni sono:

  • di Conway ?: esempio è più leggibile e più semplice per me che il if/elsif forma, ma ho potuto vedere come la forma potrebbe diventare difficile da capire.

  • Se avete Perl 5.13.1, utilizzare my $t=do { given { when } }; come un incarico come rafi has done. penso given/when è il miglior linguaggio ora, a meno che il formato tabulare ternario è meglio per il vostro caso particolare.

  • Se Perl 5.10+ utilizza given/when in generale anziché Switch o se è necessario un tipo di interruttore del tipo di caso.

  • Perl precedente, questo è un modulo fine per semplici alternative o come alternativa a un'istruzione case. È meglio che usare Switch, penso.

  • L'associatività da destra a sinistra indica che il modulo viene valutato dal basso verso l'alto. Ricorda che quando si utilizza ...

risposta

15

Il layout illustrato per l'operatore condizionale è difficile da leggere. Questo è più simile a quello che mi ricordo Perl Best Practices raccomandare:

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

E ci sono momenti in cui è meglio usare una notazione più compatta con la notazione if, troppo:

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

Entrambi questi reformattings sottolineano la somiglianza delle varie sezioni del codice, rendendola più facile da leggere e capire.

+0

Mi piace la tua formattazione per entrambe le alternative e la forma condizionale che mi hai ancora letto più facilmente di if/elsif. Cosa ne pensi? – dawg

+0

@drewk: sono entrambi abbastanza leggibili; la forma dell'operatore condizionale ha una singola assegnazione, quindi è chiaro che è assegnato un singolo valore, mentre con la versione if-chain una delle azioni potrebbe essere "$ l =" qualcosa ";" "e l'errore di battitura nel nome della variabile potrebbe rimanere senza macchie per un po '. Quindi, sì, penso che ci siano vantaggi per l'operatore condizionale - ma non è una preferenza terribilmente forte. –

19

Ho visto questo idioma utilizzato in Perl. Poiché l'operatore ternario ? : è, beh, un operatore, è documentato in perlop, non perlsyn.

Ai miei occhi, è una specie di Perl idiomatico, ma lo scopo principale di questo in Perl sembra evitare la mancanza di un'appropriata dichiarazione switch mentre non si scrivono enormi caselle if/else. Tuttavia, questo è stato risolto anni fa in perl 5.10.0. In questi giorni non riesco a vedere molte ragioni per non aver scritto quanto sopra, come questo, che sembra essere molto più leggibile rispetto a (ab) utilizzando il ternario:

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

o, come di Perl 5.13.1, anche se :

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

Un'altra alternativa sarebbe qualcosa di simile, wich funziona su tutte le versioni di 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; 
} 

Anche se questo evita sia utilizzando enormi if/elsif/else -cascades, e non ha bisogno di caratteristiche di r ecent perls, probabilmente non vale lo sforzo per questo semplice esempio. Tuttavia, posso vedere un approccio come questo essere utile con molte condizioni e con il vincolo di voler supportare vecchi perls.

(Si noti inoltre che l'esempio iniziale non gestisce $ n essendo inferiore a zero o non essendo affatto un numero.)

Nota del CJM: si può anche fare questo in tutte le versioni di 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

Sei sempre stato in grado di utilizzare il valore restituito di 'if' in un blocco' do'. Ho aggiunto un esempio. È solo 'dato' in 5.10 e 5.12 che non lo consente. – cjm

+0

Buon punto. Grazie! :-) – rafl

+1

Non è * l'operatore * ternario, è * un * operatore ternario. È * l'operatore condizionale *. – Ether

5

Ho visto il incatenato condizionali un po ', lo usavano a volte, e lo odiavano sempre. È utile ma brutto a meno che non si arrivi agli estremi per formattarlo e semplificare le espressioni interstiziali.

Non sono così difficili da capire quando li incontri un paio di volte e capisci che è un idioma. È più facile capire una vera e propria dichiarazione di switch. C'è anche meno possibilità di posizionare male i due punti e incasinare tutto in un modo difficile.