Operador ternário considerado prejudicial? [fechadas]


79

Por exemplo, você prefere essa linha única

int median(int a, int b, int c) {
    return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
}

ou uma solução if / else envolvendo várias instruções de retorno?

Quando é ?:apropriado e quando não é? Deve ser ensinado ou escondido dos iniciantes?


221
Este uso particular de que é :)
karmajunkie

6
Quem codificou isso e como é a versão de uma mediana para quatro números? Ou cinco?
Mason Wheeler

3
O nome mais correto é 'operador condicional'. Por acaso é o operador ternário mais comum em uso.
Alan Pearce

1
Isso foi solicitado há mais de dois anos no stackoverflow. Vamos pedir novamente tudo por aqui agora? stackoverflow.com/questions/160218/to-ternary-or-not-to-ternary
webbiedave

3
Estou surpreso por que essas perguntas continuam surgindo. A resposta é sempre "o que funciona e é legível". - o último sendo igualmente importante.
Apoorv Khurasia

Respostas:


234

O operador ternário é mau?

Não, é uma benção.

Quando é?: Apropriado?

Quando é algo tão simples, você não quer perder muitas linhas.

e quando não é?

Quando a legibilidade e a clareza do código sofrem e o potencial de erro por atenção insuficiente aumenta, por exemplo, com muitos operadores encadeados, como no seu exemplo.


O teste decisivo é quando você começa a duvidar que seu código seja facilmente legível e mantenível a longo prazo. Então não faça isso.


23
+1 para Quando a legibilidade e a clareza do código sofrem. Com muitos operadores encadeados, como no seu exemplo. O exemplo leva mais tempo para entender do que o equivalente se / outro.
sange

23
+1 bravo pela ótima explicação! Os desenvolvedores não tendem a perceber que algumas coisas são chamadas de julgamento, querem que tudo seja preto e branco. Isso me deixa louco. Eu encontrei para muitas pessoas da opinião "X é mau, nunca vamos usá-lo". Eu prefiro "X é ótimo se você usá-lo para o que é bom".
Reponha Monica

54
Também é ruim se for usado: myVar = (someExpression)? verdadeiro falso; Aaaaarrgh!
adamk

20
@adamk: Tente isto para o mal:myVar = someExpression ? false : true;
Dean Harding

8
Como sobre (someExpression ? var1 : var2)++:-)
fredoverflow

50

Acho que o operador ternário não aninhado (ou seja, uma declaração em que é usada apenas uma vez) é bom, mas se você estiver aninhando mais de um, fica um pouco difícil de ler.


3
Isso pode ser considerado uma simplificação excessiva, mas é uma diretriz muito fácil de seguir e funciona na maioria dos casos.
Alan Pearce

2
Na verdade, é minha regra de ouro: você nunca deve aninhá-los; caso contrário, substitua-os por if / else, será mais claro assim.
Piovezan

Se você os usasse e os aninhasse, então, pelo amor à humanidade, use parênteses e espaços em branco para torná-los legíveis. If-else pode ser feito tão feio. Resista ao desejo de mostrar como você é 'inteligente' escrevendo coisas que os compiladores podem ler, mas os humanos não. Algum dia você será o humano que não pode.
Candied_orange 29/08

24

Quando é?: Apropriado

  • Quando torna seu código mais conciso e legível.

e quando não é?

  • Quando torna seu código ilegível.
  • Se você estiver fazendo isso apenas para agradar uma ferramenta de refatoração como o ReSharper e não a pessoa que precisa manter o código

Se você tem alguma chamada lógica ou de função dentro da expressão ternária, está tornando isso horrível de se ver.


Este merece muitos votos positivos!
Piovezan

22

Uma diferença que (acho) ninguém apontou é que o if-else não pode retornar um valor, enquanto o operador ternário pode.

Vindo de F #, às vezes gosto de usar o operador ternário para imitar a correspondência de padrões.

match val with
| A -> 1
| B -> 3
| _ -> 0

vs

return val == A ? 1 : 
       val == B ? 3 : 
       0;

Isso é bem legal. Nunca pensei nisso.
Rei Miyasaka

+1 @ Benjol: Eu ia apontar a mesma coisa (que em F #, tudo é uma expressão, incluindo se / elif / else). Também uso ternários como no seu exemplo, até agora também não descobertos :). Outra coisa que tenho feito, em Javascript, talvez exagerada, é: var res = function() {switch(input) {case 1: return "1"; case 2: return "2"; ...}}()emular switches como expressões.
Stephen Swensen

@Stephen, eu estava indo para usar as palavras expressione statementmas estou sempre preocupado que eu vou levá-los de forma errada rodada e fazer papel de bobo :)
Benjol

@ Benjol: Eu sei o que você quer dizer!
Stephen Swensen

6
Também é útil em C e C ++ para inicializar constvariáveis ​​que não podem ser alteradas posteriormente.
precisa

13

Um exemplo (IMHO) de um uso válido:

printf("Success in %d %s\n", nr_of_tries, (nr_of_tries == 1 ? "try" : "tries"));

Isso resulta em um código mais legível do que em duas instruções de impressão distintas. Exemplos aninhados dependem: (compreensível? Sim: não)


11
Lembre-se de que, se você fizer isso, estará fazendo um inferno para quem precisa localizar seu aplicativo. Claro, se isso não for um problema, vá em frente.
Anon.

1
Essa é a minha regra de ouro: 1 nível de aninhamento (em algumas circunstâncias).
Oliver Weiler

7

Absolutamente não é mau. De fato, é puro , e se-então-outro não é.

Em linguagens funcionais como Haskell, F #, ML etc., são as declarações if-then-else que são consideradas más.

A razão para isso é que qualquer "ação", como uma instrução imperativa se-então-outro, exige que você separe uma declaração variável de sua definição e introduza estado em sua função.

Por exemplo, no seguinte código:

const var x = n % 3 == 1
    ? Parity.Even
    : Parity.Odd;

vs.

Parity x;
if (n % 3 == 1)
    x = Parity.Even;
else
    x = Parity.Odd;

O primeiro tem duas vantagens além de ser mais curto:

  1. x é uma constante e, portanto, oferece muito menos chances de introduzir bugs, e pode ser otimizado de maneiras que o segundo nunca poderia ser.
  2. O tipo é esclarecido pela expressão, para que o compilador possa deduzir sem esforço que xprecisa ser do tipo Parity.

De maneira confusa, nas linguagens funcionais, o operador ternário costuma ser chamado se-então-outro. Em Haskell, você pode dizer x = if n mod 3 == 1 then Odd else Even.


Sim, esse também é o argumento que @Benjol fez. Veja meu comentário na resposta dele para uma maneira divertida de emular instruções de switch como expressões em Javascript.
Stephen Swensen

7

Essa expressão em particular faz meus olhos doerem; Eu atacaria qualquer desenvolvedor da minha equipe que o usasse porque é impossível de manter.

Os operadores ternários não são maus quando bem utilizados. Eles nem precisam ser linhas simples; Uma longa e bem formatada pode ser muito clara e fácil de entender:

return
      ( 'a' == $s ) ? 1
    : ( 'b' == $s ) ? 2
    : ( 'c' == $s ) ? 3
    :                 4;

Eu gosto disso melhor do que a cadeia if / then / else equivalente:

if ( 'a' == $s ) {
    $retval = 1;
}
elsif ( 'b' == $s ) {
    $retval = 2;
}
elsif ( 'c' == $s ) {
    $retval = 3;
}
else {
    $retval = 4;
}

return $retval;

Vou reformatá-los em:

if    ( 'a' == $s ) { $retval = 1; }
elsif ( 'b' == $s ) { $retval = 2; }
elsif ( 'c' == $s ) { $retval = 3; }
else                { $retval = 4; }

return $retval;

se as condições e atribuições permitirem um alinhamento fácil. Ainda assim, prefiro a versão ternária, porque é mais curta e não apresenta tanto ruído nas condições e atribuições.


Por que não consigo inserir quebras de linha nos comentários? Arrgghh!
Christopher Mahan

3

O ReSharper no VS.NET às vezes sugere a substituição if...elsepelo ?:operador.

Parece que o ReSharper sugere apenas se as condições / blocos estiverem abaixo de um determinado nível de complexidade, caso contrário, ele permanecerá if...else.


4
outro ótimo recurso que eu amo sobre ReSharper
Anonymous Type

2

Isso pode ser reformatado para ser tão bonito quanto a combinação if / else:

int median(int a, int b, int c)
{
    return
        (a<b)
        ?
            (b<c)
            ? b
            :
                (a<c)
                ? c
                : a
        :
            (a<c)
            ? a
            :
                (b<c)
                ? c
                : b;
}

Mas o problema é que não tenho certeza se entendi direito o recuo para representar o que realmente acontecerá. :-)


3
+1 Acho que isso é muito melhor do que uma if-elseestrutura equivalente . A chave é a formatação.
Orbling

13
Se eu vi isso em uma base de código, consideraria seriamente procurar um novo emprego.
Nick Larsen

+1 Não para esse recuo real, mas esta é a melhor solução para este exemplo.
Mark Hurd

2
Só que uma outra formatação automática acidental iria acabar com tudo o que o recuo, e tempo para mais uma rodada de reestruturação - :) extremamente produtiva
Nawfal

2

Longe de ser mau, o operador ternário é uma dádiva de Deus.

  • É mais útil quando você deseja tomar uma decisão em uma expressão aninhada . O exemplo clássico é uma chamada de função:

    printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
    
  • No seu exemplo particular, o ternário é quase gratuito, pois é a expressão de nível superior sob a return. Você pode elevar o condicional ao nível da instrução sem duplicar nada além da returnpalavra - chave.

NB: Nada nunca tornará esse algoritmo específico para mediana fácil de ler.


É difícil ser tão legível que você não pode melhorar. printf("I see %d evil construct%s in this program\n", n, "s" unless (n == 1) "s");
Pacerier

2
  1. Argumentos "malignos" à parte, na minha experiência, encontrei uma alta correlação entre o uso de um programador pelo operador ternário e a probabilidade de toda a sua base de código ser difícil de ler, seguir e manter (se não totalmente não documentada). Se um programador está mais preocupado em salvar algumas linhas de um a dois caracteres do que alguém capaz de entender seu código, qualquer pequena confusão em entender uma declaração ternária é geralmente a ponta do iceberg.

  2. Os operadores ternários atraem números mágicos como s ** t atrai moscas.

Se eu estivesse procurando uma biblioteca de código-fonte aberto para resolver um problema específico, e vi código como os operadores ternários do pôster original em um candidato para essa biblioteca, os sinos de aviso começariam a soar na minha cabeça e eu começaria a pensar em seguir em frente para algum outro projeto para emprestar.


2

Aqui está um exemplo de quando é mau:

oldValue = newValue >= 0 ? newValue : oldValue;

É confuso e inútil. O compilador pode otimizar a segunda expressão (oldValue = oldValue), mas por que o codificador fez isso em primeiro lugar?

Outro doozy:

thingie = otherThingie != null ? otherThingie : null;

Algumas pessoas simplesmente não pretendem ser codificadores ...

Greg diz que a instrução if equivalente é 'barulhenta'. É se você escrever ruidosamente. Mas o mesmo se pode ser escrito como:

if ('a' == $s) return 1;
if ('b' == $s) return 2;
if ('c' == $s) return 3;
return 4;

O que não é mais barulhento que o ternário. Eu me pergunto se os atalhos ternários; todas as expressões são avaliadas?


Seu segundo exemplo faz-me lembrar if (x != 0) x = 0;...
fredoverflow

2

Mal? Olha, eles são apenas diferentes.

ifé uma afirmação. (test ? a : b)é uma expressão. Eles não são a mesma coisa.

Expressões existem para expressar valores. Existem instruções para executar ações. Expressões podem aparecer dentro de instruções, mas não vice-versa. Portanto, você pode usar expressões ternárias em outras expressões, como termos em um somatório ou argumentos para um método, e assim por diante. Você não precisa , mas pode, se quiser . Não há nada de errado nisso. Algumas pessoas podem dizer que isso é mau, mas essa é a opinião delas.

Um valor de uma expressão ternária é o fato de lidar com casos verdadeiros e falsos. ifdeclarações não.

Se você estiver preocupado com a legibilidade, poderá formatá-los de forma legível.

De alguma forma, o "mal" entrou no vocabulário de programação. Eu adoraria saber quem largou primeiro. (Na verdade, eu tenho um suspeito - ele está no MIT.) Prefiro que tenhamos razões objetivas para julgamentos de valor nesse campo, não apenas o gosto das pessoas e os xingamentos.


1
Posso dar uma dica de quem é o suspeito? Só para melhorar um pouco o meu conhecimento do campo.
precisa saber é o seguinte

1
@mlvljr: Bem, eu posso estar errado, então é melhor não.
precisa saber é o seguinte

1

Tem um lugar. Já trabalhei em muitas empresas nas quais os níveis de habilidade dos desenvolvedores variam de terrível a assistente. Como o código precisa ser mantido e eu não estarei lá para sempre, tento escrever coisas para que pareçam pertencer a ele (sem olhar para os comentários com minhas iniciais, é extremamente raro você poder veja o código em que trabalhei para ver onde fiz as alterações) e que alguém com menos habilidade do que eu pode mantê-lo.

Embora o operador ternário pareça nitziffic e bem legal, minha experiência é que a linha de código será praticamente impossível de manter. No meu atual empregador, temos produtos que são entregues há quase 20 anos. Eu não usaria esse exemplo em nenhum lugar.


1

Não acho que o operador ternário seja mau.

Aqui está uma pegadinha que me deixou perplexo. Eu era um programador C para muitos (10+) e, no final dos anos 90, mudei para a programação de aplicativos baseados na Web. Como programador da Web, logo encontrei o PHP, que também possui um operador ternário. Eu tive um bug em um programa PHP que finalmente rastreiei para uma linha de código com um operador ternário aninhado. Acontece que o operador ternário do PHP associou da esquerda para a direita, mas o operador ternário do PHP (com o qual eu estava acostumado) associa da direita para a esquerda.


1

Qualquer coisa que torne seu código mais feio é ruim.

Se você usar ternário para tornar seu código mais limpo, use-o com certeza. Às vezes, no php, é ótimo fazer substituições em linha, por exemplo

"Hello ".($Male?"Mr":"Ms")." $Name

Isso salva algumas linhas, e é bem claro, mas seu exemplo precisa de uma formatação pelo menos melhor para ficar claro, e ternário não é realmente bom para várias linhas, então você pode usar if / else.


1

Posso dizer isso? Não consigo encontrar esse aplicativo específico da operação ternária evil :

  1. a operação que ele executa é bastante trivial, e uma vez que você passou por ela uma vez, dificilmente é possível que alguns erros ocorram;
  2. o que ele faz está claramente indicado no nome da função;
  3. tomar> 1 linha para algo tão óbvio e que tão claramente não seria melhorado no futuro (a menos que um algoritmo de mediana mágica não tenha sido detectado até agora).

Por favor, tenha piedade, minha reputação já é bastante lamentável.


1

Maior vitória: mostra que existe um único alvo de ação.

if ( $is_whatever )
    $foo = 'A';
else
    $foo = 'B';

Existem dois caminhos de código que você pode seguir e o leitor deve ler atentamente para ver quais duas variáveis ​​estão sendo definidas. Nesse caso, é apenas uma variável, mas o leitor precisa ler mais para descobrir isso. Afinal, poderia ter sido isso:

if ( $is_whatever )
    $foo = 'A';
else
    $bar = 'B';

Com o operador ternário, fica claro que apenas uma variável está sendo definida.

$foo = $is_whatever ? 'A' : 'B';

No nível mais baixo, é o princípio DRY (não se repita) em sua forma mais básica. Se você pode especificar $fooapenas uma vez, faça-o.


0

Se ... então ... o resto tende a enfatizar a condição e, portanto, desenfatiza as operações condicionais.

o operador ternário é o oposto, tende a ocultar a condição e, portanto, é útil quando a operação que está sendo realizada é mais importante que a própria condição.

Há algumas pequenas diferenças técnicas, em algumas linguagens, que elas não são totalmente intercambiáveis ​​devido a uma ser uma declaração e outra uma expressão, por exemplo, inicializar condicionalmente consts em C ++


0

Quando é apropriado e quando não é?

Eu acho que ao desenvolver para um grupo homogêneo de pessoas, não há problema, mas quando você tem que lidar com pessoas que lidam com níveis diferentes, esse tipo de oneliners apenas introduz um outro nível de complexyti no código. Portanto, minha política sobre o assunto é: código claro e não explica, em vez de código curto, e explica 123123 vezes.

Deve ser ensinado ou escondido dos iniciantes?

Eu não deveria ser ensinado a iniciantes, preferia que eles descobrissem quando a necessidade surgisse; portanto, ela será usada somente quando necessário e não sempre que você precisar.


0

Na IMO, o próprio operador não é ruim, mas a sintaxe usada em C (e C ++) é excessivamente concisa. OMI, Algol 60 fez melhor, então algo assim:

A = x == y ? B : C;

ficaria mais parecido com este (mas mantendo a sintaxe do tipo C em geral):

A = if (x==y) B else C;

Mesmo com isso, o aninhamento excessivamente profundo pode levar a problemas de legibilidade, mas pelo menos A) qualquer pessoa que já tenha feito programação pode descobrir um processo simples e B) as pessoas que entendem isso podem lidar com o aninhamento consideravelmente mais profundo com bastante facilidade. OTOH, eu também observaria que no LISP (por exemplo) a condé quase como uma declaração ternária - não um conjunto de instruções, mas uma única expressão gera um valor (então, novamente, a maior parte do LISP é assim .. .)


Por que não fazer isso apenas para facilitar a leitura? A = (x==y) ? B : C
Jeremy Heiler

@ Jeremy: enquanto algumas pessoas acham as parênteses úteis, mesmo na melhor das hipóteses elas não ajudam muito . Aninhe mais do que um par de profundidade e você ainda precisará de um recuo cuidadoso (no mínimo) para manter as coisas organizadas. Sem dúvida, o mesmo aconteceria eventualmente, em Algol, mas eu nunca digo um problema surgir nele como muitas vezes eu fazer em C ...
Jerry Coffin

Apenas presumi que todos concordavam que aninhar um operador ternário é ruim. Eu estava falando especificamente sobre os exemplos que você forneceu. Em particular, como o primeiro pode ser mais parecido com o segundo na maioria dos idiomas.
precisa saber é o seguinte

0

Uma loja que escreve regularmente métodos de 600 a 1200 linhas não deve me dizer que um ternário é "difícil de entender". Qualquer loja que permita regularmente cinco condições para avaliar uma ramificação de código não deve me dizer que as condições resumidas concretamente em um ternário são "difíceis de ler".


0

Quando é?: Apropriado e quando não é?

  • Se você não obtiver um ganho de desempenho, não o use; isso afeta a legibilidade do seu código.
  • Use-o uma vez e não aninhe-o.
  • É mais difícil depurar.

Deve ser ensinado ou escondido dos iniciantes?

Não importa, mas não deve ser intencionalmente oculto, pois não é muito complexo para um "iniciante" aprender.


-2

No seu exemplo:

def median(a, b, c):
    if a < b < c: return b
    if a < c < b: return c
    if b < a < c: return a
    if b < c < a: return c
    if c < a < b: return a
    if c < b < a: return b

é muito simples de ler e óbvio. A variável entre <<é o valor de retorno.

Atualizar

mesmo, mas menos linhas de código. Ainda simples, eu acho.

def median(a, b, c):
    if b<a<c or c<a<b: return a
    if a<b<c or c<b<a: return b
    if a<c<b or b<c<a: return c

Isso requer 12 comparações no pior caso ...
fredoverflow

1
Talvez, mas é legível.
precisa

-2

Também é necessário para const

const int nLegs  = isChicken ? 2: 4 ;

Estranho. Eu acho que é C ++ ou algo assim. Pensei const sempre foi constante tempo de compilação (como em C #)
Nawfal

@nawfal - se você não sabe isChicken até a execução
Martin Beckett

sim é isso que. Eu acho que consté assim em certas línguas. Em C # constdeve sempre ser o valor conhecido do tempo de compilação. o que significa que const int nLegs = isChicken ? 2: 4 ;não vai funcionar, mas const int nLegs = true ? 2: 4 ;vai
Nawfal
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.