Operador ternario considerado perjudicial? [cerrado]

79

Por ejemplo, ¿preferirías esta línea ú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;
}

o una solución if / else que involucra múltiples declaraciones de devolución?

¿Cuándo es ?:apropiado y cuándo no? ¿Debería enseñarse u ocultarse a los principiantes?

FredOverflow
fuente
221
Este uso particular es :)
karmajunkie
66
¿Quién codificó eso y cómo es su versión de una mediana para cuatro números? O cinco?
Mason Wheeler
3
El nombre más correcto es 'operador condicional'. Simplemente resulta ser el operador ternario más común en uso.
Alan Pearce
1
Esto se hizo hace más de dos años en stackoverflow. ¿Vamos a volver a preguntar todo por aquí ahora? stackoverflow.com/questions/160218/to-ternary-or-not-to-ternary
webbiedave
3
Me sorprende por qué siguen surgiendo esas preguntas. La respuesta siempre es "lo que funciona y es legible". El último es igualmente importante.
Apoorv Khurasia

Respuestas:

234

¿Es malo el operador ternario?

No, es una bendición.

¿Cuándo es? ¿Apropiado?

Cuando es algo tan simple que no quieres desperdiciar muchas líneas.

y cuando no?

Cuando la legibilidad y la claridad del código se resienten y aumenta la posibilidad de un error por falta de atención, por ejemplo, con muchos operadores encadenados, como en su ejemplo.


La prueba de fuego es cuando comienzas a dudar de que tu código sea fácil de leer y mantener a largo plazo. Entonces no lo hagas.

usuario8685
fuente
23
+1 para cuando la legibilidad y claridad del código sufre. Con muchos operadores encadenados, como en su ejemplo. El ejemplo tarda más en comprender que el equivalente if / else's.
Sange
23
+1 bravo por la gran explicación! Los desarrolladores no tienden a darse cuenta de que algunas cosas son decisiones de juicio, quieren que todo sea blanco y negro. Me vuelve loco. Me he encontrado con muchas personas de la opinión "X es malvado, nunca lo usemos". Prefiero "X es genial si lo usas para lo que es bueno".
Vuelva a instalar Mónica
54
También es malo si se usa: myVar = (someExpression)? verdadero Falso; Aaaaarrgh!
adamk
20
@adamk: Prueba esto para el mal:myVar = someExpression ? false : true;
Dean Harding
8
¿Qué tal (someExpression ? var1 : var2)++:-)
fredoverflow
50

Creo que el operador ternario sin verificar (es decir, una declaración donde se usa solo una vez) está bien, pero si anida más de uno, se vuelve algo difícil de leer.

revs mipadi
fuente
3
Esto podría considerarse una simplificación excesiva, pero es una guía muy fácil de seguir y funciona en la mayoría de los casos.
Alan Pearce
2
En realidad, es mi regla de oro: nunca debes anidarlos, de lo contrario reemplazarlos con if / else's, será más claro de esa manera.
Piovezan
Si los usaría y los anidaría, entonces, por amor a la humanidad, use paréntesis y espacios en blanco para que sea legible. If-else se puede hacer igual de feo. Resista el impulso de mostrar cuán "inteligente" es usted escribiendo cosas que los compiladores pueden leer pero los humanos no. Algún día serás el humano que no puede.
candied_orange
24

¿Cuándo es?: Apropiado

  • Cuando hace que su código sea más conciso y legible.

y cuando no?

  • Cuando hace que tu código sea ilegible.
  • Si lo hace solo para complacer a una herramienta de refactorización como ReSharper y no a la persona que tiene que mantener el código

Si tiene alguna llamada lógica o de función dentro de la expresión ternaria, es horrible verla.

codificador de mundo real
fuente
¡Este merece muchos votos a favor!
Piovezan
22

Una diferencia que (creo) que nadie ha señalado es que si no puede devolver un valor, mientras que el operador ternario sí.

Viniendo de F #, a veces me gusta usar el operador ternario para imitar la coincidencia de patrones.

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

vs

return val == A ? 1 : 
       val == B ? 3 : 
       0;
revs Benjol
fuente
Eso es muy bonito. Nunca pensé en eso.
Rei Miyasaka
+1 @Benjol: Iba a señalar lo mismo (que en F #, todo es una expresión, incluido if / elif / else). También uso ternaries como en tu ejemplo, hasta ahora también sin descubrir :). Otra cosa que he estado haciendo, en Javascript, quizás por encima, es: var res = function() {switch(input) {case 1: return "1"; case 2: return "2"; ...}}()emular interruptores como expresiones.
Stephen Swensen
@Stephen, iba a usar las palabras expressiony statementsiempre me preocupa que las
interprete
@Benjol: ¡Sé lo que quieres decir!
Stephen Swensen
66
También es útil en C y C ++ para inicializar constvariables que no se pueden cambiar más adelante.
David Thornley
13

Un ejemplo (en mi humilde opinión) de un uso válido:

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

Esto da como resultado un código más legible que tener 2 declaraciones de impresión distintas. Los ejemplos anidados dependen: (¿comprensible? Sí: no)

Henno Brandsma
fuente
11
Solo tenga en cuenta que si hace esto, está haciendo un infierno para quien tenga que localizar su aplicación. Por supuesto, si eso no es un problema, adelante.
Anon
1
Esa es mi regla general: 1 nivel de anidamiento (en algunas circunstancias).
Oliver Weiler
7

Absolutamente no malvado. De hecho, es puro , y si no lo es.

En lenguajes funcionales como Haskell, F #, ML, etc., son las declaraciones if-then-else las que se consideran malvadas.

La razón de esto es que cualquier "acción" como una declaración imperativa if-then-else requiere que separe una declaración de variable de su definición e introduce el estado en su función.

Por ejemplo, en el siguiente 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;

El primero tiene dos ventajas además de ser más corto:

  1. x es una constante y, por lo tanto, ofrece muchas menos posibilidades de introducir errores, y puede optimizarse de una manera que el segundo nunca podría ser.
  2. La expresión deja claro el tipo, por lo que el compilador puede deducir sin esfuerzo que xdebe ser de tipo Parity.

Confusamente, en lenguajes funcionales, el operador ternario a menudo se llama if-then-else. En Haskell, se podría decir x = if n mod 3 == 1 then Odd else Even.

Rei Miyasaka
fuente
sí, este es el punto que hizo @Benjol también. Vea mi comentario a su respuesta para una forma divertida de emular declaraciones de cambio como expresiones en Javascript.
Stephen Swensen
7

Esa expresión particular me duele los ojos; Atacaría a cualquier desarrollador de mi equipo que lo usara porque no se puede mantener.

Los operadores ternarios no son malos cuando se usan bien. Ni siquiera tienen que ser líneas simples; Una larga que esté bien formateada puede ser muy clara y fácil de entender:

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

Me gusta más que la cadena if / then / else equivalente:

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

return $retval;

Los reformatearé en:

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

return $retval;

si las condiciones y las tareas permiten una fácil alineación. Aún así, prefiero la versión ternaria porque es más corta y no tiene tanto ruido en torno a las condiciones y tareas.

el hombre de hojalata
fuente
¿Por qué no puedo poner saltos de línea en los comentarios? Arrgghh!
Christopher Mahan
3

ReSharper en VS.NET a veces sugiere reemplazar if...elsecon el ?:operador.

Parece que ReSharper sugiere solo si las condiciones / bloques están por debajo de un cierto nivel de complejidad, de lo contrario se mantiene if...else.

Uwe Keim
fuente
44
otra gran característica que me encanta de ReSharper
tipo anónimo el
2

Esto se puede reformatear para que sea tan atractivo como la combinación 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;
}

Pero el problema es que no estoy realmente seguro si obtuve la sangría correcta para representar lo que realmente sucederá. :-)

Zan Lynx
fuente
3
+1 Creo que esto es mucho mejor que una if-elseestructura equivalente . La clave es el formateo.
Orbling
13
Si lo viera en una base de código, consideraría seriamente buscar un nuevo trabajo.
Nick Larsen
+1 No para esta sangría real, pero esta es la mejor solución para este ejemplo.
Mark Hurd el
2
Solo que otro formateo automático accidental eliminaría toda esa sangría, y el tiempo para otra ronda de reestructuración - muy productiva :)
nawfal
2

Lejos de ser malvado, el operador ternario es un regalo del cielo.

  • Es más útil cuando desea tomar una decisión en una expresión anidada . El ejemplo clásico es una llamada de función:

    printf("I see %d evil construct%s in this program\n", n, n == 1 ? "" : "s");
    
  • En su ejemplo particular, el ternario es casi gratuito, ya que es la expresión de nivel superior bajo a return. Puede levantar el condicional al nivel de instrucción sin duplicar nada más que la returnpalabra clave.

NB Nada hará que ese algoritmo particular para la mediana sea fácil de leer.

Norman Ramsey
fuente
Es difícil ser tan legible que no se puede mejorar. printf("I see %d evil construct%s in this program\n", n, "s" unless (n == 1) "s");
Pacerier
2
  1. Dejando a un lado los argumentos "malvados", en mi experiencia he encontrado una alta correlación entre el uso de un programador del operador ternario y la probabilidad de que toda su base de código sea difícil de leer, seguir y mantener (si no es indocumentada). Si un programador está más preocupado por guardar unas pocas líneas de 1-2 caracteres que alguien que pueda entender su código, entonces cualquier confusión menor que comprenda una declaración ternaria suele ser la punta del iceberg.

  2. Los operadores ternarios atraen números mágicos como s ** t atrae moscas.

Si estaba buscando una biblioteca de código abierto para resolver un problema particular, y veía código como los operadores ternarios del póster original en un candidato para dicha biblioteca, las campanas de advertencia comenzarían a sonar en mi cabeza y comenzaría a considerar seguir adelante a otro proyecto para pedir prestado.

usuario8865
fuente
2

Aquí hay un ejemplo de cuándo es malo:

oldValue = newValue >= 0 ? newValue : oldValue;

Es confuso y derrochador. El compilador podría optimizar la segunda expresión (oldValue = oldValue), pero ¿por qué el codificador hizo esto en primer lugar?

Otra doozy:

thingie = otherThingie != null ? otherThingie : null;

Algunas personas simplemente no están destinadas a ser codificadores ...

Greg dice que el enunciado if equivalente es 'ruidoso'. Es si lo escribes ruidosamente. Pero eso mismo si se puede escribir como:

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

Que no es más ruidoso que el ternario. Me pregunto si los atajos ternarios; Cómo se evalúan todas las expresiones?

hapybrian
fuente
Su segundo ejemplo me recuerda if (x != 0) x = 0;...
fredoverflow
2

¿Mal? Mira, son simplemente diferentes.

ifEs una declaración. (test ? a : b)es una expresion. No son lo mismo.

Existen expresiones para expresar valores. Existen declaraciones para realizar acciones. Las expresiones pueden aparecer dentro de las declaraciones, pero no al revés. Por lo tanto, puede usar expresiones ternarias dentro de otras expresiones, como para términos en una suma, o para argumentos de un método, y así sucesivamente. No tiene que hacerlo , pero puede hacerlo si lo desea . No hay nada de malo en eso. Algunas personas pueden decir que eso es malo, pero esa es su opinión.

Un valor de una expresión ternaria es que le permite manejar casos verdaderos y falsos. iflas declaraciones no lo hacen.

Si le preocupa la legibilidad, puede formatearlos de manera legible.

De alguna manera, el "mal" se introdujo en el vocabulario de programación. Me encantaría saber quién lo dejó caer por primera vez. (En realidad, tengo un sospechoso: está en el MIT). Preferiría que tuviéramos razones objetivas para los juicios de valor en este campo, no solo el gusto de las personas y los insultos.

Mike Dunlavey
fuente
1
¿Puedo tener una pista sobre quién es el sospechoso? Solo para mejorar un poco mi conocimiento del campo.
mlvljr
1
@mlvljr: Bueno, podría estar equivocado, así que mejor no hacerlo.
Mike Dunlavey
1

Tiene un lugar. He trabajado en muchas compañías donde los niveles de habilidad de los desarrolladores varían desde terribles hasta magos. Dado que el código debe mantenerse y no estaré allí para siempre, trato de escribir cosas para que parezca que pertenece allí (sin mirar los comentarios con mis iniciales, es extremadamente raro que pueda mira el código en el que he trabajado para ver dónde hice los cambios) y que alguien con menos habilidad que yo pueda mantenerlo.

Si bien el operador ternario se ve nitido y genial, mi experiencia es que la línea de código será casi imposible de mantener. En mi empleador actual, tenemos productos que se han estado enviando durante casi 20 años. No usaría ese ejemplo en ningún lado.

Tangurena
fuente
1

No creo que el operador ternario sea malo.

Sin embargo, aquí hay una trampa que me dejó perplejo. Fui programador en C para muchos (10+) y a fines de la década de 1990 me mudé a la programación de aplicaciones basadas en la web. Como programador web, pronto me encontré con PHP, que también tiene un operador ternario. Tuve un error en un programa PHP que finalmente rastreé hasta una línea de código con un operador ternario anidado. Resulta que el operador ternario PHP asociado de izquierda a derecha, pero el operador ternario C (al que estaba acostumbrado) se asocia de derecha a izquierda.

leed25d
fuente
1

Cualquier cosa que haga tu código más feo es malo.

Si usa ternary para hacer su código más limpio, entonces úselo. A veces, como PHP, es genial hacer sustituciones en línea, por ejemplo

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

Eso ahorra algunas líneas, y es bastante claro, pero su ejemplo necesita al menos un mejor formato para ser claro, y el ternario no es realmente bueno para líneas múltiples, entonces también podría usar if / else.

usuario11134
fuente
1

¿Puedo decirlo? No puedo encontrar esta aplicación particular de la operación ternaria evil :

  1. la operación que realiza es bastante trivial, y una vez que la atraviesas una vez, es casi imposible que salgan algunos errores;
  2. lo que hace se indica claramente en el nombre de la función;
  3. tomar> 1 línea para algo tan obvio y que claramente no mejoraría en el futuro (a menos que un algoritmo mediano mágico haya vivido sin ser detectado hasta ahora).

Por favor, ten piedad, mi reputación ya es bastante lamentable.

cbrandolino
fuente
1

Mayor victoria: muestra que hay un único objetivo de acción.

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

Hay dos rutas de código que puede seguir, y el lector debe leer cuidadosamente para ver qué dos variables se están configurando. En este caso, es solo una variable, pero el lector tiene más que leer para resolverlo. Después de todo, podría haber sido esto:

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

Con el operador ternario, está claro que solo se está configurando una variable.

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

En el nivel más bajo, es el principio SECO (No repetir) en su forma más básica. Si $foosolo puede especificar una vez, hágalo.

Andy Lester
fuente
0

Si ... entonces ... más tiende a enfatizar la condición y, por lo tanto, desestima las operaciones que se realizan condicionalmente.

el operador ternario es lo contrario, tiende a ocultar la condición y, por lo tanto, es útil cuando la operación que se realiza es más importante que la condición misma.

Existe un pequeño inconveniente técnico, en algunos lenguajes, de que no son intercambiables debido a que uno es una declaración y otro una expresión, por ejemplo, inicializando condicionalmente consts en C ++

jk.
fuente
0

¿Cuándo es apropiado y cuándo no?

Creo que cuando se desarrolla para un grupo homogéneo de personas, no hay ningún problema, pero cuando se tiene que tratar con personas que manejan diferentes niveles, este tipo de línea solo introduce un nivel adicional de complexyti al código. Entonces, mi política al respecto es: código claro y no explicar en lugar de código corto y explicar 123123 veces.

¿Debería enseñarse u ocultarse a los principiantes?

No debería enseñarme a los principiantes, más bien los prefiero para que lo descubran cuando surja la necesidad, por lo que se usará solo cuando sea necesario y no cada vez que necesite un if.

Guiman
fuente
0

En mi opinión, el operador en sí no es malo, pero la sintaxis utilizada para él en C (y C ++) es excesivamente breve. En mi opinión, Algol 60 lo hizo mejor, así que algo como esto:

A = x == y ? B : C;

se parecería más a esto (pero se adhiere a la sintaxis tipo C en general):

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

Incluso con eso, el anidamiento excesivamente profundo puede conducir a problemas de legibilidad, pero al menos A) cualquiera que haya hecho la programación puede resolverlo de manera simple, y B) las personas que lo entienden pueden manejar un anidamiento considerablemente más profundo con bastante facilidad. OTOH, también notaría que en LISP (por ejemplo) a condes más o menos como una declaración ternaria, no un conjunto de declaraciones, sino una sola expresión que produce un valor (de nuevo, la mayoría de LISP es así ... .)

Jerry Coffin
fuente
¿Por qué no solo hacer esto para facilitar la lectura? A = (x==y) ? B : C
Jeremy Heiler el
@Jeremy: mientras que algunas personas encuentran útiles a los padres, incluso en el mejor de los casos no ayudan mucho . Anida más de un par de profundidad y aún necesitas una sangría cuidadosa (como mínimo) para mantener las cosas ordenadas. Indudablemente, lo mismo sucedería eventualmente en Algol, pero nunca digo que surja un problema como lo hago a menudo en C ...
Jerry Coffin
Simplemente asumí que todos estaban de acuerdo en que anidar un operador ternario es malo. Estaba hablando específicamente sobre los ejemplos que proporcionó. En particular, cómo el primero puede ser más parecido al segundo en la mayoría de los idiomas.
Jeremy Heiler el
0

Una tienda que escribe regularmente métodos de 600-1200 líneas no debería decirme que un ternario es "difícil de entender". Cualquier tienda que regularmente permita cinco condiciones para evaluar una rama de código no debería decirme que las condiciones concretamente resumidas en un ternario son "difíciles de leer".

Axeman
fuente
0

¿Cuándo es?: ¿Apropiado y cuándo no?

  • Si no obtiene un aumento de rendimiento, no lo use; afecta la legibilidad de su código.
  • Úselo una vez y no lo anide.
  • Es más difícil de depurar.

¿Debería enseñarse u ocultarse a los principiantes?

No importa, pero no debe ocultarse intencionalmente ya que no es demasiado complejo para que un "principiante" aprenda.

Amir Rezaei
fuente
-2

En tu ejemplo:

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

es muy simple de leer y obvio. La variable entre <<es el valor de retorno.

Actualizar

igual, pero menos líneas de código. Todavía simple, creo.

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
Christopher Mahan
fuente
Esto requiere 12 comparaciones en el peor de los casos ...
fredoverflow
1
Quizás, pero es legible.
Christopher Mahan el
-2

También es necesario para const

const int nLegs  = isChicken ? 2: 4 ;
Martin Beckett
fuente
Extraño. Creo que es C ++ o algo así. Pensé que const siempre había sido constante en el tiempo de compilación (como en C #)
nawfal
@nawfal - si no sabes isChicken hasta el tiempo de ejecución
Martin Beckett
si eso es lo que Creo que constes así en ciertos idiomas. En C # constsiempre se debe compilar el tiempo de valor conocido. lo que significa que const int nLegs = isChicken ? 2: 4 ;no funcionará, pero lo const int nLegs = true ? 2: 4 ;hará
nawfal