compuesto de estilo Y / O sentencias if

13

¿Cómo se diseñan las instrucciones AND / OR if complejas compuestas para una legibilidad máxima? ¿Cómo se sangra y dónde se colocan los saltos de línea? Mi situación particular es algo como lo siguiente. Definitivamente es mejor que romper todo en una sola línea, pero todavía se ve desordenado.

if (
    (
        x == y
        && a != b
        && p.isGood() 
        && (
            i + u == b
            || q >= a
        )
    )
    || k.isSomething()
    || m > n
) {
    doSomething();
}
    
pregunta JoJo 17.06.2011 - 00:12

8 respuestas

6

Haz variables booleanas para cada pequeño paso:

bool step1 = i + u == b || q >= a;
bool step2 = a != b && p.isGood() && group1;
bool step3 = group2 || k.isSomething() || m > n;
if (step3) { doSomething(); }

Por supuesto, esto es similar a la respuesta de Lacrymology, excepto con nombres diferentes para cada paso.

Si nombras step1 , step2 y step3 de manera que tenga un buen sentido conceptual, esto debería ser el más legible. p.isGood() y k.isSomething() a veces pueden invocarse en situaciones en las que no estaría en su código original, por lo que no sería una opción si esas funciones son caras o si está ejecutando este código en un bucle muy ajustado .

Por otra parte, no tiene que preocuparse por el impacto en el rendimiento en el que incurra la creación de nuevas variables; un buen compilador los optimizará.

Un ejemplo con detección de colisión de rectángulo (que probablemente no usaría debido al acierto de rendimiento mencionado anteriormente):

if((a.x + a.width >= b.x || b.x + b.width >= a.x)
 && (a.y + a.height >= b.y || b.y + b.width >= a.y)
)
{ collision(); }

Podría convertirse en:

bool horizMatch = a.x + a.width >= b.x || b.x + b.width >= a.x;
bool vertMatch = a.y + a.height >= b.y || b.y + b.width >= a.y;
if(horizMatch && vertMatch) { collision(); }

Además, si desea dejar su código como está, creo que eso también sería totalmente correcto. Sinceramente, creo que su código es bastante legible. Obviamente, no sé qué es exactamente a b x y i u p k m n , pero en lo que respecta a la estructura, me parece bien.

    
respondido por el Rei Miyasaka 17.06.2011 - 01:46
8

Por lo general, reclasifico mi código para que sea más modular si mis condiciones condicionan eso.

    
respondido por el JohnFx 17.06.2011 - 00:23
8

Haría algo más como esto, en este nivel de complejidad

bool doIt = x == y && a != b && p.isGood();
doIt &= ( i + u == b || q >= a);
doIt |= k.isSomething() || m > n;

if(doIt)
{
    doSomething();
}

es feo, pero es legible y estoy bastante seguro de que el compilador sabrá cómo refactorizarlo.

Por otra parte, si alguna vez me veo en la situación de escribir una declaración IF de este tipo, reconsideraré la solución, porque estoy CIERTO que hay una forma de hacerlo más simple, o al menos abstraer algo de esa condición ( por ejemplo: tal vez x == y && a != b && p.isGood() realmente significa this->isPolygon() y puedo hacer ese método;

    
respondido por el Lacrymology 17.06.2011 - 00:50
4

Me estoy obsesionando menos con la alineación vertical con el tiempo, pero mi forma general con expresiones multilínea es ...

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6)
       )
    && (   (expr7 == expr8)
        || (expr9 == expra)
       )
   )
{
  blah;
}

Puntos clave ...

  • Los parens cercanos se alinean verticalmente con los parens abiertos, al igual que con las llaves.
  • Las sub-expresiones que caben dentro de una línea están dentro de una línea y están alineadas verticalmente a la izquierda. Cuando ayuda a la legibilidad, los operadores de infijo dentro de esas partes de una sola línea también están alineados verticalmente.
  • Las llaves de cierre naturalmente crean líneas casi en blanco, lo que ayuda a agrupar visualmente las cosas.

A veces, formateo + y * u otros operadores como este también. Unas pocas expresiones complejas toman una forma de suma de producto o producto de suma (que puede referirse a "sumas" y "productos" booleanos), por lo que probablemente sea lo suficientemente común como para que valga la pena un estilo consistente.

Ten cuidado con esto, sin embargo. A menudo es mejor refactorizar (mover partes de la expresión a una función, o calcular y almacenar partes intermedias en una variable) en lugar de usar sangría para tratar de hacer que una expresión demasiado compleja sea más legible.

Si prefieres apilar tus parches cercanos en el lado derecho, no lo odio , pero supongo que no está tan mal. Tomado demasiado lejos, corres el riesgo de que un error pueda dejar una sangría que tergiversa lo que hacen los paréntesis, sin embargo.

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6))

    && (   (expr7 == expr8)
        || (expr9 == expra)))
{
  blah;
}
    
respondido por el Steve314 17.06.2011 - 02:55
1

enlace

Estoy de acuerdo con la respuesta de JohnFx y con la de Lacrymology. Construiría un montón de funciones (preferiblemente estáticas) que cumplen objetivos pequeños y luego las construiré de manera inteligente.

Entonces, ¿qué tal algo como esto? Tenga en cuenta que esta no es la solución perfecta, pero funciona. Hay formas de limpiar esto aún más, pero se necesita información más específica. Nota: este código debe ejecutarse con la misma rapidez, ya que el compilador es inteligente.

// Currently based on members or global vars
// (which is often a bad idea too)
function doSomethingCondirionally()
{
  if (k.isSomething() || m > n)
  {
    doSomething();
    return;
  }

  // Else ... 
  if (x != y) return;
  if (a == b) return;
  if (!p.isGood()) return;

  // Final, positive check
  if (i + u == b || q >= a)
  {
    doSomething();
  }
}
    
respondido por el Job 17.06.2011 - 01:22
1

Por lo que vale, me sorprendió ver que su ejemplo se parece mucho a los complicados predicados que he escrito. Estoy de acuerdo con los demás en que un predicado complicado no es el mejor para la facilidad de mantenimiento o la legibilidad, pero a veces aparecen.

Permítame enfatizar que hizo esta parte correctamente: && a != b NUNCA coloque el conector lógico al final de una línea, es muy fácil omitirlo visualmente. Otro lugar en el que NUNCA se debe colocar un operador al final de la línea es en concatenación de cadenas, en idiomas con dicho operador.

Haz esto:

String a = b
   + "something"
   + c
   ;

No hagas esto:

String a = b +
   "something" +
   c;
    
respondido por el Bruce Ediger 17.06.2011 - 02:03
0

Si el condicional es tan complicado, generalmente es una indicación de que se debe dividir en partes. Quizás una cláusula puede ser asignada a una variable intermedia. Tal vez una cláusula puede convertirse en un método auxiliar. Generalmente prefiero no tener tantos ands y ors en una línea.

    
respondido por el Zhehao Mao 17.06.2011 - 03:06
0

Podrías dividir el código en varias declaraciones, haciéndolo fácil de entender. Pero un ninja real haría algo como esto. :-)

if
(
    (
        x == y
    &&
        a != b
    &&
        p.isGood()
    &&
        (
            i + u == b
        ||
            q >= a
        )
    )
||
    k.isSomething()
||
    m > n
)
{
    doSomething();
}
    
respondido por el Daniel Lubarov 17.06.2011 - 03:17

Lea otras preguntas en las etiquetas