我有一种情况,理论上总是会达到嵌套在两个return
循环中的for
语句。
编译器不同意并且需要在return
循环之外的for
语句。我想知道一种优雅的方法来优化这种方法,这超出了我目前的理解,并且我尝试的中断实现似乎都不起作用。
Attached是一种来自赋值的方法,它生成随机整数并返回循环的迭代,直到找到第二个随机整数,在作为int参数传递给方法的范围内生成。
private static int oneRun(int range) {
int[] rInt = new int[range+1]; // Stores the past sequence of ints.
rInt[0] = generator.nextInt(range); // Inital random number.
for (int count = 1; count <= range; count++) { // Run until return.
rInt[count] = generator.nextInt(range); // Add randint to current iteration.
for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
if (rInt[i] == rInt[count]) {
return count;
}
}
}
return 0; // Never reached
}
编译器的启发式方法永远不会让你省略最后的return
。如果你确定它永远不会到达,我会用throw
替换它以使情况清楚。
private static int oneRun(int range) {
int[] rInt = new int[range+1]; // Stores the past sequence of ints.
rInt[0] = generator.nextInt(range); // Inital random number.
for (int count = 1; count <= range; count++) {
...
}
throw new AssertionError("unreachable code reached");
}
private static int oneRun(int range) {
int result = -1; // use this to store your result
int[] rInt = new int[range+1]; // Stores the past sequence of ints.
rInt[0] = generator.nextInt(range); // Inital random number.
for (int count = 1; count <= range && result == -1; count++) { // Run until result found.
rInt[count] = generator.nextInt(range); // Add randint to current iteration.
for (int i = 0; i < count && result == -1; i++) { // Check for past occurence and leave after result found.
if (rInt[i] == rInt[count]) {
result = count;
}
}
}
return result; // return your result
}
作为@BoristheSpider pointed out,您可以确保第二个return
语句在语义上无法访问:
private static int oneRun(int range) {
int[] rInt = new int[range+1]; // Stores the past sequence of ints.
int count = 0;
while (true) {
rInt[count] = generator.nextInt(range); // Add randint to current iteration.
for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
if (rInt[i] == rInt[count]) {
return count;
}
}
count++;
}
}
编译并运行良好。如果你得到一个ArrayIndexOutOfBoundsException
,你会知道实现在语义上是错误的,而不必明确地抛出任何东西。
由于您询问了两个for
循环的中断,您可以使用标签来执行此操作(请参阅下面的示例):
private static int oneRun(int range) {
int returnValue=-1;
int[] rInt = new int[range+1]; // Stores the past sequence of ints.
rInt[0] = generator.nextInt(range); // Inital random number.
OUTER: for (int count = 1; count <= range; count++) { // Run until return.
rInt[count] = generator.nextInt(range); // Add randint to current iteration.
for (int i = 0; i < count; i++) { // Check for past occurence and return if found.
if (rInt[i] == rInt[count]) {
returnValue = count;
break OUTER;
}
}
}
return returnValue;
}
虽然断言是一个很好的快速解决方案。一般来说,这种问题意味着您的代码太复杂了。当我查看你的代码时,显然你并不真的想要一个数组来保存以前的数字。你想要一个Set
:
Set<Integer> previous = new HashSet<Integer>();
int randomInt = generator.nextInt(range);
previous.add(randomInt);
for (int count = 1; count <= range; count++) {
randomInt = generator.nextInt(range);
if (previous.contains(randomInt)) {
break;
}
previous.add(randomInt);
}
return previous.size();
现在请注意,我们返回的内容实际上是集合的大小。代码复杂度从二次变为线性,并且立即更具可读性。
现在我们可以意识到我们甚至不需要那个count
指数:
Set<Integer> previous = new HashSet<Integer>();
int randomInt = generator.nextInt(range);
while (!previous.contains(randomInt)) {
previous.add(randomInt);
randomInt = generator.nextInt(range);
}
return previous.size();
由于您的返回值基于外部循环的变量,您可以简单地将外部循环的条件更改为count < range
,然后在函数末尾返回最后一个值(您刚刚省略):
private static int oneRun(int range) {
...
for (int count = 1; count < range; count++) {
...
}
return range;
}
这样您就不需要引入永远无法访问的代码。
使用临时变量,例如“result”,并删除内部返回。使用适当的条件更改while循环的for循环。对我来说,只有一个返回作为函数的最后一个语句总是更优雅。
也许这表明您应该重写代码。例如:
具有return语句并在其中包含循环/循环的方法总是需要在循环外部使用return语句。即使永远不会达到循环之外的这种说法。在这种情况下,为了避免不必要的返回语句,您可以定义相应类型的变量,在您的情况下为整数,在方法的开头,即在相应的循环之前和之外。当达到循环内的所需结果时,您可以将相应的值归因于此预定义变量,并将其用于循环外的return语句。
由于你希望你的方法在rInt [i]等于rInt [count]时返回第一个结果,所以仅实现上述变量是不够的,因为当rInt [i]等于rInt [count]时,该方法将返回最后的结果。一个选项是实现两个“break语句”,当我们得到所需的结果时调用它们。所以,该方法看起来像这样:
private static int oneRun(int range) {
int finalResult = 0; // the above-mentioned variable
int[] rInt = new int[range + 1];
rInt[0] = generator.nextInt(range);
for (int count = 1; count <= range; count++) {
rInt[count] = generator.nextInt(range);
for (int i = 0; i < count; i++) {
if (rInt[i] == rInt[count]) {
finalResult = count;
break; // this breaks the inside loop
}
}
if (finalResult == count) {
break; // this breaks the outside loop
}
}
return finalResult;
}
我同意应该抛出无法访问语句的异常。只是想展示相同的方法如何以更易读的方式执行此操作(需要java 8流)。
private static int oneRun(int range) {
int[] rInt = new int[range + 1];
return IntStream
.rangeClosed(0, range)
.peek(i -> rInt[i] = generator.nextInt(range))
.filter(i -> IntStream.range(0, i).anyMatch(j -> rInt[i] == rInt[j]))
.findFirst()
.orElseThrow(() -> new RuntimeException("Shouldn't be reached!"));
}