Выполнение кода между циклами итераций

Скажем, вы хотите перебрать 3 элемента следующим образом:

for(int i=0; i<3; i++)
{
    doSomething();
}

Конечно, это то же самое, что сказать: doSomething(); doSomething(); doSomething();,

Теперь предположим, что вы хотите сделать что-то МЕЖДУ каждой итерацией, как если бы вы кодировали это:doSomething(); doBetween(); doSomething(); doBetween(); doSomething();

Обратите внимание, как doSomething() называется 3 раза, но doBetween() называется 2 раза.

Прямо сейчас, единственный способ, которым я знаю, как сделать это в цикле:

for(int i=0; i<3; i++)
{
    doSomething();
    if(i<2)
        doBetween();
}

Мне кажется неэффективным запускать это условие в цикле. Это также заставляет вас смотреть на него дважды, чтобы понять замысел программирования. Кроме того, если вы измените "3" на что-то другое в заголовке "for", вы можете легко забыть изменить условное выражение, особенно по мере роста логики. Более того, этот трюк не будет работать в цикле foreach, потому что нет простого способа определить, выполняем ли мы последнюю итерацию.

Какие у вас есть советы для того, чтобы сделать что-то подобное таким образом, чтобы повысить производительность, удобочитаемость, удобство обслуживания или цикл foreach?

6 ответов

Решение

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

    public static IEnumerable<TSource> ForEach<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, Action<TSource> action, Action<TSource> between)
    {
        bool first = true;
        foreach (TSource item in source)
        {
            if (first) first = false; else between();
            action(item);
        }
        return source;
    }

Это будет называться так:

myList.ForEach(i => DoSomething(), i => DoBetween());

Или же

Enumerable.Range(0, 3).ForEach(i => DoSomething(), i => DoBetween());

Как насчет чего-то простого?

for(int i = 0; i < 3; i++) {
    doSomething();
    doBetween();
}
doSomething();
for(int i = 0; i < 3; i++) {
    if(i > 0) {
        doBetween();
    }
    doSomething();
}
{
    int i=0;

    if(i<3){
        while(true){
            doSomething();
            i++;
            if(i<3){
                doBetween();
            }
            else{
                break;
            }

        }
    }
}

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

Предполагая, что вы хотите использовать doSomething и doBetween для всего, что вы просматриваете в foreach:

bool firstComplete = false;

foreach(Item i in ItemList)
{
   if (firstComplete)
   {
      doBewteen(i);
   }
   else
   {
      firstComplete = true;
   }

   doSomething(i);
}

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

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

Я обычно использую логический флаг для этого:

bool first = true;
for (int i = 0; i < 3; i++) {
  if (first) {
    first = false;
  } else {
    doBetween();
  }
  doSomething();
}

Вы также можете рассмотреть возможность удаления одной итерации из цикла:

doSomething();
for (int i = 1; i < 3; i++) {
  doBetween();
  doSomething();
}

Однако, если цикл может быть пустым (ноль элементов), вам придется сначала проверить это.

Это позволит вам определить начальную и конечную точки перед входом в цикл...

int start = 0;
int limit = 3;
for (int i == start; i < limit; i++) {
    doSomething();
    if (i > start && i < limit - 1) {
        doBetween();
    }
}
Другие вопросы по тегам