Как избежать бесполезного возврата в Java-методе
У меня есть ситуация, когда теорема о возврате, вложенная в два цикла, всегда будет достигнута теоретически. Компилятор не согласен и требует выражения 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
}
Ответы
Ответ 1
Эвристика компилятора никогда не позволит вам опустить последний 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");
}
Ответ 2
Как @BoristheSpider указал, вы можете убедиться, что второй оператор 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
, вы поймете, что реализация была семантически неправильной, без необходимости явно бросать что-либо.
Ответ 3
Поскольку вы спросили о выходе из двух циклов 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;
}
Ответ 4
Хотя утверждение является хорошим быстрым решением. В общем, такие проблемы означают, что ваш код слишком сложный. Когда я смотрю на ваш код, очевидно, что вы не хотите, чтобы массив сохранял предыдущие числа. Вы хотите 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();
Ответ 5
Поскольку ваше возвращаемое значение основано на переменной внешнего цикла, вы можете просто изменить условие внешнего цикла на count < range
, а затем вернуть это последнее значение (которое вы просто пропустили) в конце функции:
private static int oneRun(int range) {
...
for (int count = 1; count < range; count++) {
...
}
return range;
}
Таким образом вам не нужно вводить код, который никогда не будет достигнут.
Ответ 6
Используйте временную переменную, например "result", и удалите внутренний возврат.
Измените цикл for для цикла while с надлежащим условием.
Для меня всегда более элегантно иметь только один возврат как последний оператор функции.
Ответ 7
Возможно, это свидетельствует о том, что вы должны переписать свой код. Например:
- Создать массив целых чисел 0.. range-1. Установите для всех значений значение 0.
- Выполните цикл. В цикле сгенерируйте случайное число. Посмотрите в своем списке, в этом индексе, чтобы узнать, равно ли значение 1. Если это так, выйдите из цикла. В противном случае установите для этого индекса значение 1
- Подсчитайте количество 1s в списке и верните это значение.
Ответ 8
Методы, которые имеют оператор return и имеют петлю/петли внутри них, всегда требуют выражения return вне цикла (ов). Даже если это утверждение вне цикла никогда не достигается. В таких случаях, чтобы избежать ненужных операторов возврата, вы могли бы определить переменную соответствующего типа, целое число в вашем случае, в начале метода, т.е. до и за пределами соответствующего цикла (ов). Когда желаемый результат внутри цикла достигнут, вы можете приписать соответствующее значение этой предопределенной переменной и использовать его для оператора return вне цикла.
Так как вы хотите, чтобы ваш метод возвращал первый результат, когда rInt [i] равно rInt [count], реализовать только вышеупомянутую переменную недостаточно, потому что метод вернет последний результат, когда rInt [i] равен rInt [ число]. Одним из вариантов является реализация двух "операторов 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;
}
Ответ 9
Я согласен с тем, что нужно исключить исключение, когда происходит недостижимый оператор. Просто хотелось показать, как один и тот же метод может сделать это более читабельным способом (требуется поток 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!"));
}
Ответ 10
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
}