Подтвердить что ты не робот

Как избежать бесполезного возврата в 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
}
4b9b3361

Ответ 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
}