Что такое аккуратный способ убежать из многих для циклов сразу?

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

рассматривают этот пример:

static IEnumerable<Person> GetAllPeople()
{
    return new List<Person>()
    {
        new Person() { Name = "George", Surname = "Bush", City = "Washington" },
        new Person() { Name = "Abraham", Surname = "Lincoln", City = "Washington" },
        new Person() { Name = "Joe", Surname = "Average", City = "New York" }
    };
}

static IEnumerable<Person> GetPeopleFrom(this IEnumerable<Person> people,  string where)
{
    foreach (var person in people)
    {
        if (person.City == where) yield return person;
    }
    yield break;
}

static IEnumerable<Person> GetPeopleWithInitial(this IEnumerable<Person> people, string initial)
{
    foreach (var person in people)
    {
        if (person.Name.StartsWith(initial)) yield return person;
    }
    yield break;
}

static void Main(string[] args)
{
    var people = GetAllPeople();
    foreach (var p in people.GetPeopleFrom("Washington"))
    {
        // do something with washingtonites
    }

    foreach (var p in people.GetPeopleWithInitial("G"))
    {
        // do something with people with initial G
    }

    foreach (var p in people.GetPeopleWithInitial("P").GetPeopleFrom("New York"))
    {
        // etc
    }
}

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

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

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

другой побочный эффект состоит в том, что урожай воздействует на самый низкий общий интерфейс набора (IEnumerable<>), который включает создание подобного библиотеке кода с широкой применимостью.

16
задан random 19 October 2009 в 04:29
поделиться

12 ответов

используйте goto. это чисто и просто.

85
ответ дан 30 November 2019 в 15:01
поделиться

Один из способов сделать это - конечный автомат. Но я бы все равно использовал goto. Все намного проще. :)

state = 0;
while( state >= 0){
    switch(state){
        case 0: i = 0; state = 1; // for i = 0
        case 1:
            i++; 
            if (i < 100)   // if for i < 100 not finished
                state = 2; // do the inner j loop
            else
                state = -1; // finish loop
        case 2: j = 0; state = 3; // for j = 0
        case 3: 
            j++;
            if (j < 100)  // if j < 100 not finished
                state = 4 // do the inner k loop
            else
                state = 1; // go backt to loop i
            break;
        case 4: k = 0; state = 5;
        case 5:
            k++;
            if (k == 50){
                state = -1;
                break;
            }
            if (k < 100) // if k loop not finished
                state = 5; // do this loop
            else
                state = 3; // go back to upper loop
            break;
        default : state = -1;
    }
}
-1
ответ дан 30 November 2019 в 15:01
поделиться

Я бы сделал что-то вроде:

  int i, j, k;

  for (i = 0; i < 100; i++) {
      for (j = 0; j < 100; j++) {
          for (k = 0; k < 100; k++) {
              if (k == 50) {
                  return;
              }
          }
      }
  }
0
ответ дан 30 November 2019 в 15:01
поделиться

Деление на 0 - самый надежный из известных мне методов, который вырвет вас из любого количества циклов. Это работает, потому что инструкция по сборке DIV не любит такой глупости.

Итак, вы можете попробовать следующее:

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                int z = 1 / 0;  // we're outta here!!!
            }
        }
        if (flag1 == 1)break;
    }
    if (flag2 == 1)break;
}

Возвращение из ловушки , которая возникает при таких событиях, оставленных в качестве упражнения для читателя (это банально).

0
ответ дан 30 November 2019 в 15:01
поделиться

немного глупого самодокументирования:

int i, j, k;
int done = 0;

for (i = 0; i < 100 && ! done; i++) {
    for (j = 0; j < 100 && ! done; j++) {
        for (k = 0; k < 100 && ! done; k++) {
            if (k == 50) we_are(done);
        }
    }
}

//...

void we_are(int *done) {
    *done = 1;
}

но на самом деле у вас не должно быть трех вложенных for- петли. Вместо этого вам следует подумать о рефакторинге различных функций и улучшении логики вашей программы.

Хотя я согласен с тем, что иногда goto действительно является лучшим решением, я думаю, что любая проблема, для которой goto решение является результатом плохого кода.

2
ответ дан 30 November 2019 в 15:01
поделиться

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

int i, j, k;
for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50)
                break;
        }
        if (k < 100) break;
    }
    if (j < 100) break;
}

По моему опыту, это то, что нужно в большинстве случаев.

2
ответ дан 30 November 2019 в 15:01
поделиться

goto . Это одно из немногих мест, где goto является подходящим инструментом, и обычно это аргумент, почему goto не является полным злом.

Однако иногда я так и поступаю. это:

void foo() {
    bar_t *b = make_bar();
    foo_helper(bar);
    free_bar(b);
}

void foo_helper(bar_t *b) {
    int i,j;
    for (i=0; i < imax; i++) {
        for (j=0; j < jmax; j++) {
            if (uhoh(i, j) {
                return;
            }
        }
    }
}

Идея состоит в том, что я гарантированно получаю бесплатный бар, плюс я получаю чистый двухуровневый выход из переключателя через возврат.

4
ответ дан 30 November 2019 в 15:01
поделиться

Чуть лучше.

int i, j, k;
int flag1 = 0;
int flag2 = 0;

for (i = 0; i < 100 && !flag2; i++) {
    for (j = 0; j < 100 && !flag1; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                flag1 = 1;
                flag2 = 1;
                break;
            }
        }
    }
}

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

5
ответ дан 30 November 2019 в 15:01
поделиться

иногда вы можете использовать такой трюк:

for (i = 0; i < 100 && !flag2; i++) {
for (j = 0; j < 100 && !flag1; j++) {
    for (k = 0; k < 100; k++) {
        if (k == 50) {
            k = 100;
            i = 100;
            j = 100;
        }
    }
}

}

или объявить флаг сложения в вашем цикле:

bool end = false;
for(int i =0; i < 1000 && !end; i++) {
   //do thing
   end = true;
}

он стоит всего одну строку, но, я думаю, чистый.

Джастин

3
ответ дан 30 November 2019 в 15:01
поделиться

How would you accomplish the same thing? (w/o using jumps)

Why? Nothing is universally evil, and every put-upon tool has its uses (except gets()). Using goto here makes your code look cleaner, and is one of the only choices we have (assuming C). Look:

int i, j, k;

for (i = 0; i < 100; i++) {
    for (j = 0; j < 100; j++) {
        for (k = 0; k < 100; k++) {
            if (k == 50) {
                goto END;
            }
        }
    }
}
END:

Much cleaner than all those flag variables, and it even shows more clearly what your code is doing.

14
ответ дан 30 November 2019 в 15:01
поделиться

Поместите все циклы в функцию и просто верните вместо break.

49
ответ дан 30 November 2019 в 15:01
поделиться

If you're using Java, you can associate labels with each for block and then reference the label after a continue statement. For example:

outerfor:
for (int i=0; i<5; i++) {
    innerfor:
    for (int j=0; j<5; j++) {
        if (i == 1 && j == 2) {
             continue outerfor;
        }
    }
}
17
ответ дан 30 November 2019 в 15:01
поделиться
Другие вопросы по тегам:

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