webentwicklung-frage-antwort-db.com.de

If-Anweisungen reduzieren

Das folgende Programm funktioniert wie erforderlich, aber wie kann ich die Anzahl der if-Anweisungen reduzieren? Mir wurde gesagt, dass, wenn Ihre Funktion 2 oder mehr If-Anweisungen enthält, Sie es falsch machen. Irgendwelche Vorschläge? Ich habe es mit switch-Anweisungen versucht, aber das hat nicht funktioniert, weil der Fall nicht boolean sein kann.

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);
    }
24
Calgar99

So erstellen Sie eine Methode für die Fälle:

 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(...);
 }

Dann haben Sie anstelle von if eine Reihe von Aufrufen der beiden Methoden. Sie können sogar eine einzelne Methode erstellen, die beide der oben genannten Methoden aufruft.

 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);
      ....
 }

Im obigen Code ist die Anzahl von ifs für mich weniger ein Problem als die Menge des wiederholten Codes.

49
John B

Hier ist eine leichte Verbesserung mit zwei switch-Anweisungen

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;
}

Leider benötigen Sie pro Divisor eine switch-Anweisung.

Alternativ können Sie OOP umarmen und zu einer Abstraktion wie folgt kommen:

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);
}

Verwendungsbeispiel:

public static void main(String[] args) {
    List<Processor> 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);
        }
    }

}
26

Im Allgemeinen ist es wahr, dass Code, der viele if-Anweisungen enthält, verdächtig wirkt. Verdächtig heißt nicht unbedingt falsch. Wenn die Problemaussage nicht zusammenhängende Bedingungen enthält, nach denen gesucht werden soll (d. H., Sie können sie nicht gruppieren), müssen Sie sie unabhängig wie Sie tun.

In Ihrem Fall müssen Sie die Teilbarkeit prüfen, ohne auf eine der anderen schließen zu können (d. H. Wenn x durch 7 teilbar ist, heißt das nicht, dass es auch durch 5 teilbar ist, usw.). Alle Zahlen, die Sie verwenden, wurden absichtlich als Primzahl gewählt, deshalb kommen Sie hier rein.

Wenn sie beispielsweise gesagt hatten, prüfen Sie die Teilbarkeit mit 2, 3 und 6. Dann könnten Sie zuerst nach 6 suchen, da Sie auch die Teilbarkeit durch 2 und 3 implizieren können. Oder umgekehrt, prüfen Sie mit 2 und 3 und implizieren Sie dies Es ist auch durch 6 teilbar. Wenn alle Zahlen Primzahlen sind, kann man einfach nicht implizieren. Ihr Code muss also alles einzeln prüfen.

Ein positiver Nebeneffekt ist, dass Sie Ihre Absicht leicht in Ihren Code einlesen können (weil alles explizit ist).

Meine zwei Cents auf diesem ...

12
mprivat

Enums passen hier gut zusammen. Sie ermöglichen es Ihnen, die Funktionalität an einem Ort zu kapseln, anstatt sie in Ihrer Flusskontrolle zu verbreiten.

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);
    }
  }

}
8
OldCurmudgeon

Ich würde behaupten, dass Sie die falsche Frage stellen. Die Frage, die Sie meiner Meinung nach stellen sollten, lautet: "Wie kann ich diesen Code so umschreiben, dass er von einem Menschen leichter verstanden wird?" 

Das Credo "Eliminieren von Aussagen" ist eine allgemeine Idee, um dies zu erreichen, hängt jedoch stark vom Kontext ab.

Die traurige Tatsache ist, dass viele der Antworten diesen sehr einfachen Algorithmus in der Form des "Vereinfachens" verschleiern. Führen Sie niemals ein Objekt ein, um einige if-Anweisungen zu entfernen. In meiner Arbeit wird der meiste Code von Leuten gepflegt, die weit weniger über Architektur, Mathematik und Code als der ursprüngliche Autor verstehen. Daher werden zusätzliche Konstrukte und Komplexität eingeführt, um den Code von 50 physischen Zeilen auf 30 physische Zeilen zu reduzieren mal schwieriger zu verstehen ist kein Gewinn.

7
John

Ich hatte angefangen, eine Antwort mit Code zu schreiben, aber viele, viele Leute schlugen mich dazu. Eine Sache, die ich noch nicht erwähnt habe, ist, dass diese bestimmte Codemetrik, auf die Sie sich beziehen, als zyklomatische Komplexität bezeichnet wird und keine schrecklich schlechte Sache ist. 

Kurz gesagt, bezieht sich dies auf die Anzahl der verschiedenen Pfade, die eine Methode einschlagen kann, wenn sie ausgeführt wird, und obwohl der Code, den Sie geschrieben haben, ziemlich hoch ist und es viele gute Tipps/Lösungen für die Reduzierung gibt, die persönlich vorgeschlagen wurden Ich würde sagen, dass der Code selbst in seiner aktuellen Form sehr gut lesbar ist - was ein Bonus ist. Es kann um einiges reduziert werden und ist immer noch lesbar, aber ich möchte sagen, dass Metriken wie diese nicht alles sind, und manchmal ist es einfacher, viele if-Anweisungen zu haben, weil sie lesbarer sind - und Lesbarkeit verringert die Fehlerwahrscheinlichkeit und macht das Debuggen wesentlich einfacher

Oh, und ich würde diesen letzten Abschnitt ersetzen:

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

Wenn Sie ein boolesches Flag wie replaced = true verwenden, wenn eine der Ersetzungsanweisungen aufgerufen wird, bricht die obige Anweisung zusammen:

if (!replaced)
      System.out.println(i);
7
Matt Taylor

Ihr Code wiederholt sich. Refactor es mit Schleifen für Ihre:

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);
    }
}
5
Bohemian

Sie können mehrere Schalter erstellen:

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;
}

Der andere Fall muss noch die if-Anweisung verwenden.
Oracle fügte die switch-Anweisung hinzu, die String in Java 7 verwendete. Vielleicht kommt die boolesche switch-Anweisung später.

1
DeadlyJesus
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");
        }
    }
}

Jetzt haben Sie weniger if

0
AmitG