Cómo reducir si las declaraciones

El siguiente progtwig funciona según sea necesario, pero ¿cómo puedo reducir la cantidad de sentencias if. Me han dicho que si su función contiene 2 o más sentencias if, entonces lo está haciendo mal. ¿Alguna sugerencia? He intentado usar sentencias de cambio, pero eso no ha funcionado porque el caso no puede ser booleano.

for(int i = 1; i < 100; i++) { if(i % 10 == 3) { System.out.println("Fizz" + "(" + i + ") 3%10"); } if(i / 10 == 3) { System.out.println("Fizz" + "(" + i + ") 3/10"); } if(i % 10 == 5) { System.out.println("Buzz" + "(" + i + ") 5%10"); } if(i / 10 == 5) { System.out.println("Fizz" + "(" + i + ") 5/10"); } if(i / 10 == 7) { System.out.println("Fizz" + "(" + i + ") 7/10"); } if(i%10 == 7) { System.out.println("Woof" + "(" + i + ") 7%10"); } if(i % 3 == 0) { System.out.println("Fizz" + "(" + i + ") 3%==0"); } if(i % 5 == 0) { System.out.println("Buzz" + "(" + i + ")5%==0"); } if(i % 7 == 0) { System.out.println("Woof" + "(" + i + ")7%==0"); } if( (i % 7 !=0 ) && (i % 3 !=0 ) && (i % 5 !=0 ) && (i % 10 !=3) && (i % 10 !=5 ) && (i%10 !=7 ) ) System.out.println(i); } 

¿Qué tal crear un método para los casos?

  public void printIfMod(int value, int mod){ if (value % 10 == mod) System.out.println(...); } public void printIfDiv(int value, int div){ if (value / 10 == div) System.out.println(...); } 

Luego, en lugar de un montón de, if tiene un conjunto de llamadas a los dos métodos. Incluso puede crear un solo método que llame a los dos anteriores.

  public void printIf(int value, int div){ printIfMod(value, div); printIfDiv(value, div); } for(int i = 1; i < 100; i++) { printIf(i, 3); printIf(i, 5); .... } 

En el código anterior, la cantidad de ifs es un problema menor para mí que la cantidad de código repetido.

Aquí hay una ligera mejora usando dos instrucciones de cambio

 switch(i / 10){ case 3: // do something break; case 5: // do something else break; case 7: // do something else break; } switch(i % 10){ case 3: // do something break; case 5: // do something else break; case 7: // do something else break; } 

Desafortunadamente, necesitarás una instrucción de cambio por divisor.

Alternativamente, puedes adoptar OOP y llegar a una abstracción como esta:

 public abstract class Processor { private final int divisor; private final int result; private final boolean useDiv; // if true, use /, else use % public Processor(int divisor, int result, boolean useDiv) { this.divisor = divisor; this.result = result; this.useDiv = useDiv; } public final void process(int i){ if ( (useDiv && i / divisor == result) || (!useDiv && i % divisor == result) ){ doProcess(i); } } protected abstract void doProcess(int i); } 

Uso de la muestra:

 public static void main(String[] args) { List processors = new ArrayList<>(); processors.add(new Processor(10, 3, false) { @Override protected void doProcess(int i) { System.out.println("Fizz" + "(" + i + ") 3%10"); } }); // add more processors here for(int i = 1; i < 100; i++){ for (Processor processor : processors) { processor.process(i); } } } 

En términos generales, es cierto que el código que tiene muchas afirmaciones if parece sospechoso. Sospechoso no necesariamente significa mal. Si la statement del problema tiene condiciones disyuntas para verificar (es decir, no puede agruparlas), entonces tiene que hacerlo de forma independiente como lo está haciendo.

En su caso, tiene que verificar la divisibilidad sin poder inferir una de la otra (es decir, si x es divisible por 7, no significa que también sea divisible por 5, etc …). Todos los números que está utilizando han sido elegidos deliberadamente como primos, así que por eso se está metiendo en esto.

Si, por ejemplo, hubieran dicho, verifique la divisibilidad entre 2, 3 y 6. Luego, primero puede verificar si hay 6 porque entonces también podría implicar la divisibilidad entre 2 y 3. O viceversa, verifique por 2 y 3 e insinúe que también es divisible por 6. Si todos los números son primos, entonces simplemente no puedes insinuar. Entonces tu código tiene que revisar todo individualmente.

Un efecto secundario positivo es que hace que su bash sea fácil de leer en su código (porque es todo explícito).

Mis dos centavos en esto …

Las enumeraciones son una buena opción aquí. Le permiten encapsular la funcionalidad en una ubicación en lugar de distribuirla en todo el control de flujo.

 public class Test { public enum FizzBuzz { Fizz { @Override String doIt(int n) { return (n % 10) == 3 ? "3%10" : (n / 10) == 3 ? "3/10" : (n / 10) == 5 ? "5/10" : (n / 10) == 7 ? "7/10" : (n % 3) == 0 ? "3%==0" : null; } }, Buzz { @Override String doIt(int n) { return (n % 10) == 5 ? "5%10" : (n % 5) == 0 ? "5%==0" : (n / 10) == 3 ? "3/10" : (n / 10) == 5 ? "5/10" : (n / 10) == 7 ? "7/10" : null; } }, Woof { @Override String doIt(int n) { return (n % 10) == 7 ? "7%10" : (n % 7) == 0 ? "7%==0" : null; } }; // Returns a String if this one is appropriate for this n. abstract String doIt(int n); } public void test() { // Duplicates the posters output. for (int i = 1; i < 100; i++) { boolean doneIt = false; for (FizzBuzz fb : FizzBuzz.values()) { String s = fb.doIt(i); if (s != null) { System.out.println(fb + "(" + i + ") " + s); doneIt = true; } } if (!doneIt) { System.out.println(i); } } // Implements the game. for (int i = 1; i < 100; i++) { boolean doneIt = false; for (FizzBuzz fb : FizzBuzz.values()) { String s = fb.doIt(i); if (s != null) { if ( doneIt ) { System.out.print("-"); } System.out.print(fb); doneIt = true; } } if (!doneIt) { System.out.print(i); } System.out.println(); } } public static void main(String args[]) { try { new Test().test(); } catch (Throwable t) { t.printStackTrace(System.err); } } } 

Comencé a escribir una respuesta relacionada con el código, pero muchas, muchas personas me dieron una paliza. Lo único que diría que no se ha mencionado aún es que esta métrica de código en particular a la que se refiere se llama complejidad ciclomática y no es algo terriblemente malo.

En resumen, se refiere a la cantidad de rutas diferentes que un método podría tomar cuando se ejecuta, y aunque es bastante alto en el código copiado que publicaste, y hay muchos buenos consejos / soluciones para reducirlo que se han sugerido, personalmente Yo diría que incluso en su forma actual, el código es muy legible, lo cual es una ventaja. Puede reducirse una cantidad justa y seguir siendo legible, pero mi punto es que las métricas como esa no lo son todo, y algunas veces puede ser más sencillo tener muchas declaraciones if porque son más legibles, y la legibilidad reduce la probabilidad de cometer errores , y hace que la depuración sea mucho más fácil

Ah, y me gustaría reemplazar esta última sección:

 if( (i % 7 !=0 ) && (i % 3 !=0 ) && (i % 5 !=0 ) && (i % 10 !=3) && (i % 10 !=5 ) && (i%10 !=7 ) ) System.out.println(i); 

Al usar un indicador booleano como se replaced = true cada vez que se llama a cualquiera de las declaraciones de reemplazo, la instrucción anterior se contrae para:

 if (!replaced) System.out.println(i); 

Yo diría que estás haciendo la pregunta equivocada. La pregunta que creo que deberías hacer es: “¿Cómo puedo reescribir este código para que un humano pueda entenderlo más fácilmente?”

El credo “eliminar si las declaraciones” es una idea general para lograr esto, pero depende en gran medida del contexto.

El hecho triste es que muchas de las respuestas confunden este algoritmo tan simple como “hacerlo más simple”. Nunca introduzca un objeto para eliminar un par de declaraciones if. En mi trabajo, la mayoría de los códigos son mantenidos por personas que comprenden mucho menos sobre architecture, matemáticas y código que el autor original, por lo que presentan construcciones y complejidad adicionales para reducir el código de 50 líneas físicas a 30 líneas físicas, pero lo hacen 4 veces más difíciles de entender no es una victoria.

Su código es repetitivo. Refactorizalo usando bucles para tu:

 for (int i = 1; i < 100; i++) { boolean found = false; // used to avoid the lengthy test for "nothing found" for (int j = 3; j <= 7; j += 2) { // loop 3, 5, 7 if (i % 10 == j) { System.out.println("Fizz" + "(" + i + ") "+j+"%10"); found = true; } if (i / 10 == j) { System.out.println("Fizz" + "(" + i + ") "+j+"/10"); found = true; } if (i % j == 0) { System.out.println("Fizz" + "(" + i + ") "+j+"%==0"); found = true; } } if (!found) { System.out.println(i); } } 

Puede crear múltiples interruptores:

 switch (i/10) { case 3: System.out.println("Fizz" + "(" + i + ") 3/10"); break; case 5: System.out.println("Fizz" + "(" + i + ") 5/10"); break; case 7: System.out.println("Fizz" + "(" + i + ") 7/10"); break; default: break; } switch (i%10) { case 3: System.out.println("Fizz" + "(" + i + ") 3%10"); break; case 5: System.out.println("Buzz" + "(" + i + ") 5%10"); break; case 7: System.out.println("Woof" + "(" + i + ") 7%10"); break; default: break; } 

El otro caso todavía tiene que usar if statement.
Oracle agregó una instrucción de cambio que usó String en Java 7. Tal vez una statement de cambio booleana vendrá más adelante.

 public class Test { public static void main(String[] args) { final int THREE = 3; final int FIVE = 5; final int SEVEN=7; final int ZERO = 0; for (int i = 1; i < 100; i++) { modOperation("Fizz", i, THREE); divideOperation("Fizz", i, THREE); modOperation("Fizz", i, FIVE); divideOperation("Buzz", i, FIVE); modOperation("Woof", i, SEVEN); divideOperation("Fizz", i, SEVEN); modOperation("Fizz", i, ZERO); divideOperation("Fizz", i, ZERO); } } private static void divideOperation(String sound, int i, int j) { if (i / 10 == j) // you can add/expand one more parameter for 10 and later on 3 in this example. { System.out.println(sound + "(" + i + ") "+j+"/10"); } } private static void modOperation(String sound, int i, int j) { if (i % 10 == j) { System.out.println(sound + "(" + i + ") "+j+"%10"); } } } 

Así que ahora tienes menos if

    Intereting Posts