2010-04-10 8 views
5

Recuerdo que hace muchos años, cuando estaba en la escuela, uno de mis profesores de informática nos enseñó que era mejor verificar la "veracidad" o "igualdad" de una condición y no las negativas como "desigualdad".Práctica de verificar la "veracidad" o la "igualdad" en las declaraciones condicionales: ¿tiene realmente sentido?

Déjenme elaborar: si se puede escribir un fragmento de código condicional comprobando si una expresión es verdadera o falsa, deberíamos verificar la 'veracidad'.

Ejemplo: averiguar si un número es impar - se puede hacer de dos maneras:

if (num % 2 != 0) 
{ 
    // Number is odd 
} 

o

if (num % 2 == 1) 
{ 
    // Number is odd 
} 

(Por favor refiérase a la respuesta marcada para un mejor ejemplo.)

Cuando comencé a codificar, sabía que num % 2 == 0 implica que el número es par, así que acabo de poner ! allí para verificar si es impar. Pero él estaba como 'No revises las condiciones NO. Tenga la práctica de verificar la "veracidad" o "igualdad" de las condiciones siempre que sea posible ". Y me recomendó que use la segunda pieza de código.

No estoy a favor o en contra, pero solo quería saber, ¿qué diferencia hay? Por favor, no responda 'Técnicamente la salida será la misma' - TODOS lo sabemos. ¿Es una práctica de programación general o es su propia práctica de programación que está predicando a otros?

NOTA: Utilicé la sintaxis de estilo C#/C++ sin motivo. Mi pregunta es igualmente aplicable cuando se utilizan los operadores IsNot, <> en VB, etc. Por lo tanto, la legibilidad del '!' operador es solo uno de los problemas. No es el problema.

+0

¿Podría ser una pregunta wiki? – slugster

Respuesta

6

El problema se produce cuando, más adelante en el proyecto, se agregan más condiciones; uno de los proyectos en los que estoy trabajando ha acumulado condiciones con el tiempo (y algunas de esas condiciones se trasladaron a etiquetas struts, luego algunas a JSTL ...) - un negativo no es difícil de leer, pero 5+ es una pesadilla, especialmente cuando alguien decide reorganizarse y negar todo. Tal vez en un nuevo proyecto, que voy a escribir:

if (authorityLvl!=Admin){ 
doA(); 
}else{ 
doB(); 
} 

comprobar de nuevo en un mes, y se ha convertido en esto:

if (!(authorityLvl!=Admin && authorityLvl!=Manager)){ 
doB(); 
}else{ 
doA(); 
} 

Sigue siendo bastante simple, pero toma un segundo más.
Ahora le da otros 5 a 10 años para pudrirse.
(x% 2! = 0) ciertamente no es un problema, pero tal vez la mejor manera de evitar el escenario anterior es enseñar a los estudiantes a no usar condiciones negativas como regla general, con la esperanza de que usen un poco juicio antes que ellos, porque decir que podría convertirse en un problema de mantenimiento probablemente no sea suficiente motivación.

Como una adición, una mejor manera de escribir el código sería:

userHasAuthority = (authorityLvl==Admin); 
if (userHasAuthority){ 
doB(); 
else{ 
doA(); 
} 

Ahora futuros codificadores son más propensos a sólo tiene que añadir "|| authorityLvl == Manager", userHasAuthority es más fácil de mover en una método, e incluso si el condicional se reorganiza, solo tendrá uno negativo. Además, nadie agregará un agujero de seguridad a la aplicación al cometer un error al aplicar la Ley De Morgan.

+0

como un rayo del cielo despejado - lo explica muy claramente - especialmente la segunda parte del código. La mejor razón que he leído. (+1 cuando obtengo suficiente rep: D) – Senthil

0

Recuerdo haber escuchado lo mismo en mis clases también. Creo que es más importante usar siempre la comparación más intuitiva, en lugar de buscar siempre la condición positiva.

0

Realmente un problema muy consecuente. Sin embargo, un aspecto negativo de la comprobación en este sentido es que solo funciona para las comparaciones binarias. Si estuviera, por ejemplo, revisando alguna propiedad de un sistema numérico ternario, estaría limitado.

1

He tenido gente que me dice que tiene que ver con qué tan "visible" es el carácter ping (!) Cuando se lee.

Si alguien habitualmente lee el código "descremado", quizás porque sienten que su velocidad de lectura normal es demasiado lenta, entonces el ! se puede perder fácilmente, lo que les da un error grave de interpretación del código.

Por otro lado, si alguien realmente lee todo el código todo el tiempo, entonces no hay ningún problema.

Dos desarrolladores muy buenos con los que he trabajado (y respeto altamente) escribirán cada uno == false en lugar de usar ! por razones similares.

El factor clave en mi mente es menos que ver con lo que funciona para usted (o para mí), y más con lo que funciona para el chico que mantiene el código. Si el código nunca será visto o mantenido por otra persona, siga su capricho personal; si el código necesita ser mantenido por otros, es mejor dirigirse más hacia la mitad de la carretera. Un menor (¡trivial!) compromiso de su parte ahora, podría salvar a alguien más por una semana de depuración más adelante.

actualización: En un nuevo examen, sugeriría factorización de la condición como una función de predicado separada daría aún mayor facilidad de mantenimiento:

if (isOdd(num)) 
{ 
    // Number is odd 
} 
+0

¡Hola, sí! Me he perdido unos cuantos '!' Al leer el código de otra persona. Especialmente si el código es como 'if (! (Some-condition))'. ¡No se me ocurrió! : D – Senthil

+0

Acerca de la actualización: no sé exactamente qué significa un predicado, pero supongo que la función isOdd() contendrá la condición, ¿no? – Senthil

+0

+1 Para la función de predicado ... Lo hago constantemente y mejora muchísimo la legibilidad. – helpermethod

2

voy a estar en desacuerdo con su viejo profesor - comprobación de un NO condición está bien siempre que esté buscando una condición NOT específica. En realidad cumple con su criterio: estarás comprobando que es cierto que un valor NO es algo.

Sin embargo, entiendo lo que quiere decir: la mayoría de las condiciones verdaderas serán órdenes de magnitud menores en cantidad que las condiciones NOT, por lo tanto, más fáciles de probar ya que está comprobando un conjunto de valores más pequeño.

+0

Siempre estaríamos verificando que algo sea VERDADERO eventualmente porque solo entonces se ejecutará la cláusula 'verdadera': D Eso no es lo que quise decir y ese no es su criterio. ¡WOW esto se está volviendo confuso! – Senthil

1

Usted todavía tiene que tener cuidado con este tipo de cosas:

if (num % 2 == 1) 
{ 
    // Number is odd 
} 

Si num es negativo y extraña a continuación, en función del idioma o la implementación num% 2 podría ser igual a -1. En ese sentido, no hay nada de malo en verificar la falsedad si simplifica al menos la sintaxis del cheque. Además, usar! = Es más claro para mí que simplemente! -¡ver todo el asunto como el! puede mezclarse con el paréntesis.

sólo para examinar la veracidad que tendría que hacer:

if (num % 2 == 1 || num % 2 == -1) 
{ 
    // Number is odd 
} 

Eso es sólo un ejemplo obvio. El punto es que si el uso de una negación permite un menor número de comprobaciones o hace que la sintaxis de las comprobaciones sea clara, entonces ese es claramente el camino a seguir (como en el ejemplo anterior). Al encerrarse en la comprobación de la veracidad no de repente hace que su condicional sea más legible.

+0

@Evan: La cosa de los números impares fue solo el primer ejemplo que se me vino a la mente. No pensé en todos los casos posibles. Pero espero que los lectores entiendan lo que trato de decir en la pregunta. – Senthil

+0

Entiendo. Solo digo que, en casos como este, verificar el caso falso involucra 1 verificación mientras se verifica que el verdadero puede incluir 2 comprobaciones. Buscar el caso falso parece preferible. –

0

En respuesta a Bevan (que no encajaba en un comentario):

Tienes razón. !foo no es siempre lo mismo que foo == false. Veamos este ejemplo, en JavaScript:

var foo = true, 
    bar = false, 
    baz = null; 

foo == false; // false 
!foo;   // false 
bar == false; // true 
!bar;   // true 
baz == false; // false (!) 
!baz;   // true 
0

También estoy en desacuerdo con su profesor en este caso específico. Tal vez estaba tan apegado a la buena lección en general, para evitar los aspectos negativos, donde un positivo le irá bien, que no vio este árbol para el bosque.

Aquí está el problema. Hoy en día, se le escucha, y convertir su código en:

// Print black stripe on odd numbers 
int zebra(int num) { 
    if (num % 2 == 1) { 
    // Number is odd 
    printf("*****\n"); 
    } 
} 

próximo mes, se mire de nuevo y decide que no le gusta constantes mágicas (tal vez él te enseña esta aversión también). Entonces usted cambia su código:

#define ZEBRA_PITCH 2 
[snip pages and pages, these might even be in separate files - .h and .c] 
// Print black stripe on non-multiples of ZEBRA_PITCH 
int zebra(int num) { 
    if (num % ZEBRA_PITCH == 1) { 
    // Number is not a multiple of ZEBRA_PITCH 
    printf("*****\n"); 
    } 
} 

y el mundo parece estar bien. Su salida no ha cambiado, y pasa su prueba de regresión.

Pero no has terminado. Desea apoyar cebras mutantes, cuyas rayas negras son más gruesas que sus rayas blancas. Recuerdas hace meses que originalmente lo codificaste de forma tal que tu código imprime una franja negra donde no debería haber una franja blanca, en los números pares. Entonces, todo lo que tienes que hacer es dividir por, digamos, 3, en lugar de por 2, y deberías haber terminado. ¿Derecha? Bueno:

#define DEFAULT_ZEBRA_PITCH 2 
[snip pages and pages, these might even be in separate files - .h and .c] 
// Print black stripe on non-multiples of pitch 
int zebra(int num, int pitch) { 
    if (num % pitch == 1) { 
    // Number is odd 
    printf("*****\n"); 
    } 
} 

Oye, ¿qué es esto? Ahora tiene en su mayoría cepas blancas donde esperaba que fueran en su mayoría negras.

El problema aquí es cómo pensar en los números. ¿Es un número "impar" porque no es par, o porque cuando se divide por 2, el resto es 1? A veces, su dominio problemático sugerirá una preferencia por uno, y en esos casos le sugiero que escriba su código para expresar ese modismo, en lugar de obsesionarse con reglas simplistas como "no probar las negaciones".

Cuestiones relacionadas