web-dev-qa-db-ja.com

ifステートメントを減らす方法

以下のプログラムは必要に応じて機能しますが、ifステートメントの量を減らすにはどうすればよいですか。あなたの関数に2つ以上のif文が含まれていると、それが間違っていると言われました。助言がありますか? switchステートメントを使用してみましたが、ケースをブール値にすることはできないため、機能しません。

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

ケースのメソッドを作成するのはどうですか?

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

次に、ifの束の代わりに、2つのメソッドの呼び出しのセットがあります。上記の両方を呼び出す単一のメソッドを作成することもできます。

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

上記のコードでは、ifsの数は、繰り返されるコードの量よりも問題が少ないです。

49
John B

これは、2つのswitchステートメントを使用したわずかな改善です。

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

残念ながら、除数ごとに1つのswitchステートメントが必要になります。

または、OOPを採用して、次のような抽象化を考え出すこともできます。

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

使用例:

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

一般的に言って、多くのifステートメントを含むコードは疑わしいように見えます。疑わしいとは必ずしも間違っているという意味ではありません。問題の文にチェックするためのばらばらの条件がある場合(つまり、それらをグループ化できない場合)、実行しているように個別に実行する必要があります。

あなたの場合、お互いを推論することができずに可分性をチェックする必要があります(つまり、xが7で割り切れる場合でも、5で割り切れるわけではありません...)。あなたが使用しているすべての数値は意図的に素数に選ばれているので、これがあなたがこれに入る理由です。

たとえば、彼らが言っていた場合、2、3、および6で可分性を確認します。次に、2と3で可分性を暗示することもできるので、最初に6を確認することができます。また、6で割り切れます。すべての数値が素数である場合は、暗示することはできません。したがって、コードはすべてを個別にチェックする必要があります。

1つのプラスの副作用は、意図がコードで読みやすくなることです(すべて明示的であるため)。

これについて私の2セント...

12
mprivat

ここでは列挙型が適しています。フロー制御全体に機能を分散させるのではなく、1つの場所に機能をカプセル化できます。

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

私はあなたが間違った質問をしていると主張します。私があなたが尋ねるべきだと思う質問は、「人間がより簡単に理解できるように、このコードをどのように書き直すことができますか?」です。

信条「除去ifステートメント」はこれを達成するための一般的なアイデアですが、コンテキストに大きく依存します。

悲しい事実は、回答の多くがこの非常に単純なアルゴリズムを「単純化する」ことを装って難読化していることです。いくつかのifステートメントを削除するオブジェクトを導入しないでください。私の仕事では、ほとんどのコードは、元の作成者ほどアーキテクチャ、数学、コードについて理解していない人によって保守されているため、追加の構成と複雑さを導入して、コードを50物理行から30物理行に削減しますが、4理解するのがさらに困難な時代は勝利ではありません。

7
John

私はコードに関する答えを書き始めましたが、多くの人が私をそれに負けました。まだ言及されていない1つのことは、あなたが参照しているこの特定のコードメトリックは サイクロマティック複雑度 と呼ばれ、ひどく悪いことではないということです。

要するに、メソッドが実行されたときにメソッドがたどることができるさまざまなパスの数を指し、投稿されたコードは非常に高いが、それを減らすための多くの優れたヒント/解決策が個人的に提案されている私はそれが現在の形式であっても、コードは非常に読みやすいと主張します-これはおまけです。それはかなり削減でき、それでも読みやすいですが、私の指摘は、そのようなメトリックがすべてではないことです。読みやすいため、多くのifステートメントを含める方が簡単な場合があります-読みやすさが低下します間違いをする可能性があり、デバッグがはるかに簡単になります

ああ、そして私はこの最後のセクションを置き換えます:

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

replaced = trueなどのブールフラグを使用して、いずれかの置換ステートメントが呼び出されると、上記のステートメントは次のように折りたたまれます。

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

コードは反復的です。あなたのループを使用してそれをリファクタリングしてください:

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

複数のスイッチを作成できます。

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

他のケースでは、ifステートメントを使用する必要があります。
Java 7.でStringを使用したOracleのswitchステートメントが追加されました。7。ブールのswitchステートメントが後で来る可能性があります。

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

だからあなたはより少ないifを持っています

0
AmitG