24

下面的程序根据需要运行,但如何减少 if 语句的数量。有人告诉我,如果您的函数包含 2 个或更多 if 语句,那么您做错了。有什么建议么?我尝试过使用 switch 语句,但这并没有奏效,因为 case 不能是布尔值。

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

9 回答 9

52

如何为案例创建一个方法:

 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你有一组调用这两个方法。您甚至可以创建一个调用上述两种方法的方法。

 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对我来说,重复代码的数量不是问题。

于 2013-05-21T12:18:55.220 回答
26

这是使用两个 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;
}

不幸的是,每个除数都需要一个 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);
        }
    }

}
于 2013-05-21T12:19:15.597 回答
12

一般来说,确实有很多if语句的代码看起来很可疑。可疑并不一定意味着错误。如果问题陈述有不相交的条件来检查(即你不能将它们分组),那么你必须像你正在做的那样独立地做它们。

在您的情况下,您必须检查可除性,而不能从另一个推断出一个(即,如果 x 可被 7 整除,这并不意味着它也可被 5 整除,等等...)。您使用的所有数字都被故意选择为素数,所以这就是您进入这个的原因。

例如,如果他们说,检查被 2、3 和 6 整除。那么你可以首先检查 6,因为这样你也可以暗示被 2 和 3 整除。反之亦然,检查被 2 和 3 并暗示它也可以被 6 整除。如果所有数字都是素数,那么你就不能暗示。因此,您的代码必须单独检查所有内容。

一个积极的副作用是它使您的意图易于在代码中阅读(因为它都是明确的)。

我的两分钱在这...

于 2013-05-21T12:23:53.060 回答
8

枚举非常适合这里。它们允许您将功能封装在一个位置,而不是将其分散到整个流程控制中。

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

}
于 2013-05-21T12:32:01.407 回答
7

我已经开始写一个涉及代码的答案,但是很多很多人都打败了我。我要说的还没有提到的一件事是,您所指的这个特定的代码度量被称为圈复杂度,并不是一件可怕的坏事。

简而言之,它指的是一个方法在执行时可以采用的不同路径的数量,虽然它在您发布的代码片段中相当高,并且有很多好的提示/解决方案可以减少它,个人建议我会争辩说,即使是目前的形式,代码也是非常可读的——这是一个好处。它可以减少相当多的数量并且仍然可读,但我的观点是,像这样的指标并不是一切,有时拥有大量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);
于 2013-05-21T12:27:56.037 回答
7

我会争辩说,你问错了问题。我认为你应该问的问题是:“我怎样才能重写这段代码,以便人们更容易理解它?”

信条“消除 if 语句”是实现此目的的一般思想,但它在很大程度上取决于上下文。

可悲的事实是,许多答案以“使其更简单”为幌子混淆了这个非常简单的算法。永远不要引入一个对象来消除几个 if 语句。在我的工作中,大多数代码是由对架构、数学和代码了解远少于原作者的人维护的,因此引入了额外的构造和复杂性,将代码从 50 条物理行减少到 30 条物理行,但只剩下 4倍难理解不是赢。

于 2013-05-21T13:51:56.043 回答
5

您的代码是重复的。使用循环重构它:

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);
    }
}
于 2013-05-21T12:59:14.340 回答
1

您可以创建多个开关:

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语句。
Oracle 增加String了 Java 7 中使用的 switch 语句。也许布尔 switch 语句会在以后出现。

于 2013-05-21T12:25:14.763 回答
0
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

于 2013-05-21T12:31:56.053 回答