Я услышал, что некоторое “повреждение” s не является плохой практикой. Что относительно этого?

Я часто слышал то использование breaks в Java считается плохой практикой, но после чтения некоторых потоков на Переполнении стека, я видел иначе. Многие говорят, что это приемлемо в определенных случаях.

Я немного смущен относительно того, что является плохой практикой в этом случае.

Для Euler Проекта: проблема 7, я создал код ниже. Проблема состояла в том, чтобы найти 10001-е начало.

int index = 2, count = 1, numPrime = 1;

while (true) {
    index++;

    if (isPrime(index)) {
        count = index;
        numPrime++;
    }

    if (numPrime >= 10001)
        break;
}

System.out.println(count);

Это дает корректный ответ (в 21 мс), но я пропускаю серьезное предупреждение? На 100% возможно создать некоторое время цикл без повреждения, но я нахожу, что за этим немного легче следовать.

Способ, которым я использую break; плохая практика? Я знаю, что всегда существует путь вокруг использования того, но является этим действительно настолько ужасный здесь?

Большое спасибо

Justian

Править

Вот является мой isPrime () кодом. Я мог бы также оптимизировать это, в то время как я в нем.

public static boolean isPrime(long num) {  
    if (num == 2)
        return true;

    if (num % 2 == 0 || num <= 0)
        return false;

    for (long i = 3; i * i <= num; i += 2)
        if (num % i == 0)
            return false;

    return true;
}

8
задан Zero Piraeus 22 January 2015 в 18:16
поделиться

9 ответов

В этом случае мне кажется, что было бы проще просто изменить условие while :

while (numPrime < 10001) {

Обычно это происходит, когда цикл while (true) заканчивается with

if (condition)
{
    break;
}

... хотя вам нужно проверить, выполняет ли что-нибудь еще в теле цикла continue .

В качестве альтернативы вы можете немного реструктурировать его:

int current = 1;
for (int i = 0; i < 10001; i++)
{
    current++;
    while (!isPrime(current))
    {
        current++;
    }
}

Тогда current будет ответом в конце.

Я обычно предпочитаю цикл for циклу while, когда вы пытаетесь сделать что-то определенное количество раз. В этом случае «что-то» - это «найти следующее простое число».

В программировании есть различные догмы, которые, как мне кажется, зашли слишком далеко, в том числе «одна точка выхода для метода» и «не использовать прерывание». Пишите код максимально разборчиво. Если вы смотрите на какой-то код и чувствуете, что то, что происходит, не слишком очевидно, попробуйте разработать другие способы его структурирования. Иногда это случай изменения цикла; иногда извлекает метод; иногда это инвертирует некоторую логику (сначала обработайте отрицательную ветвь, возможно, преждевременно завершите, а затем обработайте нормальный случай).

15
ответ дан 5 December 2019 в 04:39
поделиться

Я не уверен, что перерывы вообще плохая практика, но я думаю, что это так.

Это немного глупо, потому что в этом нет необходимости. Ваш while (true) ... break полностью эквивалентен:

while (numPrime < 10001) {
   ...
}

, но гораздо менее интуитивно понятен.

Приверженность каноническому способу представления вещей означает меньшие вычислительные затраты для ваших читателей, делает ваш код более понятным, а значит, более простым в обслуживании и, в конечном итоге, более надежным.

Редактировать (в ответ на комментарий): Вы правы, что всегда есть способ использовать один, но правило (если вы его хотите) относительно простое: пишите то, что наиболее читаемо. В случае, который вы опубликовали, это не самый читаемый вариант, поскольку я дал альтернативу каноническим способом представить это. В некоторых случаях, например, в цикле по коллекции до тех пор, пока вы не найдете подходящего кандидата, использование break , вероятно, является наиболее подходящим способом представления этого шаблона.

Было бы сложно (и я буду спорить, бесполезно) пытаться придумать жесткие правила о том, когда использовать break , а когда извлекать переменные и использовать вместо них условные выражения. Часто легко сказать, какой вариант самый простой, но не всегда. Это один из тех примеров, когда действительно имеет значение опыт - чем больше хорошего / плохого кода вы прочитали и написали сами, тем больше ваша личная библиотека хороших и плохих примеров и тем легче вам будет сказать, какой из них «лучший». "* способ представить данную концепцию.

* Конечно субъективно!

22
ответ дан 5 December 2019 в 04:39
поделиться

Есть несколько условий, при которых имеет смысл использовать перерыв. Один из них - когда вам нужно выполнить цикл N.5, то есть вы выполните цикл несколько раз, но в последний раз вы всегда будете выполнять где-то в середине тела цикла. Вы можете избежать использования прерывания в этом случае, но это часто запутывает код или приводит к дублированию. Например:

while (true) {
    first part;
    if (finished) 
       break;
    second part;
}

можно преобразовать в что-то вроде:

first part;
while (!finished) {
    second part;
    first part;
}

или:

while (!finished) {
    first part;
    if (!finished)
        second part;
}

Ни то, ни другое не обязательно является серьезным улучшением. Еще одно обстоятельство, при котором перерыв может иметь смысл, - это просто иметь дело с чем-то вроде ошибки. Например, если вам передано в обработку N файлов, может иметь смысл выйти из цикла, если один из них не открывается. Тем не менее, когда это вообще разумно , явно лучше иметь условие, при котором вы будете выходить из цикла, явно указанное в условии цикла.

2
ответ дан 5 December 2019 в 04:39
поделиться

Если есть только одно условие, при котором цикл может завершиться, и нет большого количества кода, который нужно запустить перед проверкой условия, и не так много кода, который необходимо запустить после проверки условия, поместите условие в цикл.

Я действительно думаю, что для «do {} while (1);» определенно есть употребление. петля. Среди них:

  1. В случае продолжения цикла первичное условие должно иметь достаточное количество кода, выполняемого как до, так и после того, как будет проверено, что размещение кода в самом условии было бы в лучшем случае неудобным.
  2. Цикл имеет несколько условий выхода, и условие выхода, которое наиболее логично находилось бы в верхней или нижней части цикла, потребовало бы выполнения специального кода, который не должен выполняться для других условий выхода.

Некоторым людям нравится использовать флаги для обозначения условий выхода. Я склонен рассматривать такие флаги как попытку избежать структур кодирования, которые на самом деле лучше всего воплощают действие программы.

Если вас не слишком заботит скорость, можно никогда не использовать любую форму goto , преждевременный выход return или, если на то пошло, использовать более одного ], а - и это с простым условием. Просто напишите всю программу в виде конечного автомата:

void do_whatever(void)
{
  int current_state=1;
  do
  {
    next_state = 0;
    if (current_state == 1)
    {
      do_some_stuff();
      next_state = 2;
    }
    if (current_state == 2)
    {
      do_some_more_stuff();
      next_state = 3;
    }
    ...
    current_state = next_state;
  } while(current_state);
}

Иногда такое кодирование полезно (особенно если «while» можно вывести из подпрограммы do_whatever () в цикл, который запускает несколько подпрограмм с аналогичным кодом «одновременно» ). И никогда не нужно использовать что-либо вроде "goto". Но для удобочитаемости конструкции структурного программирования намного лучше.

На мой взгляд, использование флага для выхода из цикла с последующим выбором одного из нескольких фрагментов кода для выполнения в зависимости от причины выхода означает замену структурированного кода неструктурированным кодом. Если в цикле я напишу

  if (index >= numItems)
  {
    createNewItem(index);
    break;
  }

, сразу (и локально) станет ясно, что я создаю новый элемент потому, что мой индекс превышает количество элементов, и мне не нужно повторно проверять условие. Если вместо этого я буду повторять цикл до тех пор, пока что-то не найду или не закончатся элементы, тогда мне придется либо повторно проверять условие после цикла, либо добавлять тест флага к каждой итерации цикла.

0
ответ дан 5 December 2019 в 04:39
поделиться

Как отмечали другие, приведенный вами пример плох, потому что вы можете легко перенести тест в WHILE.

Случаи, когда я использую перерыв, - это когда у меня есть цикл, в котором я должен выполнить некоторую обработку, прежде чем я узнаю, что это последний раз. Например:

while (true)
{
  String inLine=inStream.readLine();
  if (inLine.startsWith("End"))
    break;
  ... process line ...
}

Этот пример лишь слегка надуманный: я часто сталкиваюсь с проблемами, когда я читаю некоторые данные из файла или другого источника, анализирую эти данные каким-либо образом и только тогда знаю, что я достиг конца того, что я меня интересует обработка прямо сейчас.

Вы, конечно, можете написать функцию, которая читает строку и выполняет необходимый синтаксический анализ, например:

while (!endOfInterestingData(inStream))
{
  ... process ...
}

Но тогда у вас может возникнуть проблема, что и функция, и тело цикла нуждаются в доступе к считанным данным. , и функция должна возвращать логическое значение для обработки цикла управления, чтобы она не могла вернуть данные, поэтому единственный способ сделать данные доступными для тела цикла - это заставить функцию перебросить их в какое-то взаимно доступное поле, которое затем скрывает, откуда цикл получает данные.

0
ответ дан 5 December 2019 в 04:39
поделиться

если вы можете уйти без перерыва, то сделайте это.

делать

 while (numPrime < 10001) ...
2
ответ дан 5 December 2019 в 04:39
поделиться

Это довольно быстрое решение:

#include <stdio.h>

unsigned long isqrt(unsigned long n) {
    // http://snippets.dzone.com/posts/show/2715
    unsigned long a;
    for (a = 0; n >= (2*a)+1; n -= (2*a++) + 1);
    return a;
}

void nPrimes(const long N, long *const primes)
{
    unsigned long n, count, i, root;

    primes[0] = 2;
    count = 1;

    for (n = 3; count < N; n+=2) {

        root = isqrt(n);

        for (i = 0; primes[i] <= root; i++) {
            if ((n % primes[i]) == 0) goto notPrime;
        }
/*      printf("Prime #%lu is: %lu\n", count+1, n);*/
        primes[count++] = n;

        notPrime: ;
    }
}

int main (int argc, char **argv)
{
    long N;

    if (argc > 1) {
        N = atoi(argv[1]);
    } else {
        N = 10001;
    }

    long primes[N];

    nPrimes(N, primes);

    printf("Prime #%lu is: %lu\n", N, primes[N-1]);
}

Примечания:

  • isqrt (n) - это пол (sqrt (n)). Это позволяет избежать операций с плавающей запятой, которые нам в любом случае не нужны. Алгоритм из http://snippets.dzone.com/posts/show/2715 .
  • nPrimes хранит список простых чисел. Это позволяет вам тестировать только простые делители, что исключает большинство дорогостоящих операций с модификациями.
  • Даже простые числа проверяются только до isqrt (n).
  • Это уродливый goto, но в C нет именованного продолжения, а использование флаговой переменной было бы пустой тратой времени.
  • Массив простых чисел инициализируется значением 2, и он проверяет только нечетные числа из 3. Удаление числа, кратного 3, аналогично выполнимо, но не тривиальной сложности.
  • Вызов isqrt можно исключить, если вести таблицу квадратов простых чисел. Это почти наверняка перебор, но вы можете, если хотите.
  • На моей не новой машине это происходит в мгновение ока.
0
ответ дан 5 December 2019 в 04:39
поделиться

Не сильно отличается от того, что было сказано ранее, но с точки зрения читабельности, прозрачной логики я бы рекомендовал использовать

long i,primes=0;
for (i=2;primes<10001;i++) {
    if (isPrime(i))
        primes++;
}

i.

2
ответ дан 5 December 2019 в 04:39
поделиться

Вот для чего был придуман do / while:

do {
//...
} while(numPrime < 10001);

Это бит while (true) , который я считаю плохой практикой, что, конечно же, приводит к перерыв .

2
ответ дан 5 December 2019 в 04:39
поделиться
Другие вопросы по тегам:

Похожие вопросы: