简体   繁体   English

如何降低圈复杂度?

[英]How do I reduce the cyclomatic complexity?

Thanks for reading my question.感谢您阅读我的问题。 I am currently taking a Java class on Coursera, and was asked to write a program on minesweeper for the assignment.我目前正在 Coursera 上一门 Java 课程,并被要求在扫雷艇上编写一个程序来完成任务。 My code creates the correct result, but my grade was deducted greatly because my code is ”excessively complex, with a cyclomatic complexity of 60“ according to the auto-grader.我的代码创建了正确的结果,但我的成绩被大大扣除了,因为根据自动评分器,我的代码“过于复杂,圈复杂度为 60”。 I understand that there are too many conditionals and loops, but I had a hard time trying to make it more simple.我知道有太多的条件和循环,但我很难让它变得更简单。

Here is my code.这是我的代码。 It takes 3 integer command-line arguments m, n, and k to create an m-by-n grid with k mines in random locations.它需要 3 个整数命令行参数 m、n 和 k 来创建一个 m×n 网格,在随机位置有 k 个地雷。 I use "5" to mark the mines instead of " " because the highest a number in a tile can get is 4 (since a tile has 4 sides).我使用“5”而不是“”来标记地雷,因为瓷砖中的最高数字是 4(因为瓷砖有 4 个边)。 If two mines are located side by side, extra values might be added to its marker of "5".如果两个地雷并排放置,则可能会为其标记“5”添加额外的值。 So I make all the values >= 5 become " " when I print them out.因此,当我打印它们时,我将所有 >= 5 的值变为“ ”。 Each value is separated by two spaces.每个值由两个空格分隔。

public class Minesweeper {
  public static void main(String[] args) {
    int m = Integer.parseInt(args[0]);
    int n = Integer.parseInt(args[1]);
    int k = Integer.parseInt(args[2]);
    int[][] mine = new int[m][n];
    //put the mines
    for(int z = 0; z < k; z++) {
      int randomX = (int) (Math.random() * m);
      int randomY = (int) (Math.random() * n);
      mine[randomX][randomY] = 5; 
    }

    for(int y = 0; y < n; y++) {
      for(int x = 0; x < m; x++) {
        //first row of the grid
        if(y == 0) {
          //upper left corner
          if(x == 0) {
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
          }
          //upper right corner
          else if(x == m - 1) {
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            } 
          }
          //mid of first row
          else {
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            } 
          }
        }
        //mid rows
        else if(y > 0 && y < n - 1) {
          //left side
          if(x == 0) {
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
          }
          //right side
          else if(x == m - 1) {
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
          }
          //mid
          else {
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y + 1] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
          } 
        }
        //bottom row
        else if(y == n - 1) {
          //bottom left corner
          if(x == 0) {
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
          }
          //bottom right corner
          else if(x == m - 1) {
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
          }
          //middle of the bottom row
          else {
            if(mine[x + 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x - 1][y] >= 5) {
              mine[x][y] += 1;
            }
            if(mine[x][y - 1] >= 5) {
              mine[x][y] += 1;
            }
          }
        }
      }
    }
  //print out the grid
    for(int y = 0; y < n; y++) {
      for(int x = 0; x < m; x++) {
        //println at the right edge of the grid
        if(x == m - 1) {
          if(mine[x][y] >= 5) {
            System.out.println("*");
          }
          else {
            System.out.println(mine[x][y]);
          }
        }
        //other tiles, no need to switch lines
        else {
          if(mine[x][y] >= 5) {
            System.out.print("*  ");
          }
          else {
            System.out.print(mine[x][y] + "  ");
          }
        }
      } 
    }
  }
}

Thank you for your time, and I'd really appreciate any suggestions.感谢您抽出宝贵时间,我非常感谢您的任何建议。

You can reduce the complexity if you do the left/right/up/down testing in a loop to save a lot of lines of code:如果在循环中进行左/右/上/下测试以节省大量代码行,则可以降低复杂性:

for (int tryX = -1; tryX <= 1; tryX++) {
  for (int tryY = -1; tryY <= 1; tryY++) {
    if(mine[x + tryX][y + tryY] >= 5) {
      mine[x][y] += 1;
    }
  }
}

Since this is taking a lot of code lines, it will reduce the complexity.由于这需要大量代码行,因此将降低复杂性。 You should extract code to methods with your IDE (see here for IntelliJ).您应该使用 IDE 将代码提取到方法中(有关 IntelliJ,请参见此处)。 Good extraction points are loops.好的提取点是循环。

I see two good extraction points:我看到两个很好的提取点:

  1. initArrayWithRandomMines() initArrayWithRandomMines()
  2. calculateNeighborMines()计算邻居地雷()

I won't share a refactored code, but give you some ideas and examples that you should do.我不会分享重构的代码,但会给你一些你应该做的想法和例子。 I think it will be more useful for your learning.我想这对你的学习会更有用。

You should find the same condition and extract them from several branches to one.您应该找到相同的条件并将它们从几个分支中提取到一个。 I recommend you start from inner conditions and after got to outside.我建议你从内部条件开始,然后到外部。

Let's take an example the branch with y == 0 condition:让我们以y == 0条件的分支为例:

if(x == 0) {
  if(mine[x + 1][y] >= 5) {
     mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  }
}
//upper right corner
else if(x == m - 1) {
  if(mine[x - 1][y] >= 5) {
     mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
     mine[x][y] += 1;
  } 
//mid of first row
else {
  if(mine[x - 1][y] >= 5) {
    mine[x][y] += 1;
  }
  if(mine[x + 1][y] >= 5) {
    mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  } 
}

You can see that you check mine[x + 1][y] >= 5 when x == 0 or in else branch.你可以看到当x == 0或在else分支时,你检查mine[x + 1][y] >= 5 You can merg two conditions in one and it will looks like x < m-1 and now code will look like:您可以将两个条件合二为一,它看起来像x < m-1 ,现在代码看起来像:

if(x < m-1 && mine[x + 1][y] >= 5) {
  mine[x][y] += 1;
}

if(x == 0) {
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  }
}
//upper right corner
else if(x == m - 1) {
  if(mine[x - 1][y] >= 5) {
     mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
     mine[x][y] += 1;
  } 
//mid of first row
else {
  if(mine[x - 1][y] >= 5) {
    mine[x][y] += 1;
  }
  if(mine[x][y + 1] >= 5) {
    mine[x][y] += 1;
  } 
}

Continue with each if statement where do you use x in condition.继续每个 if 语句,你在哪里使用x条件。 when you finish with x , do the same actions with conditions with y .当您完成x ,对y条件执行相同的操作。 Extract the common parts from several branches.从几个分支中提取公共部分。

And about part where you print the result.关于打印结果的部分。 Please think about what you should do.请想想你应该怎么做。 You should print * or mine[x][y] depends on mine[x][y]==5 and print spaces or new_line ( System.out.println() ) depends on x == m - 1 .您应该打印*mine[x][y]取决于mine[x][y]==5并打印空格或 new_line ( System.out.println() ) 取决于x == m - 1 Now think about how to implement it.现在考虑如何实现它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM