Какой самый элегантный способ зацикливаться ДВАЖДЫ в С - PullRequest
15 голосов
/ 19 октября 2010

Много раз мне нужно делать ДВАЖДЫ в цикле for.Просто я могу настроить цикл for с итератором и пройти его дважды:

for (i = 0; i < 2; i++)
{
 // Do stuff
}

Теперь мне интересно сделать это как можно проще, возможно, без инициализатора или итератора?Есть ли другие, действительно простые и изящные способы достижения этого?

Ответы [ 23 ]

59 голосов
/ 20 октября 2010

Это элегантно, потому что выглядит как треугольник;и треугольники изящны.

i = 0; 
here: dostuff(); 
i++; if ( i == 1 ) goto here;
35 голосов
/ 19 октября 2010

Инкапсулируйте его в функцию и вызовите его дважды.

void do_stuff() {
  // Do Stuff
}

// .....

do_stuff();
do_stuff();

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

30 голосов
/ 19 октября 2010

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

statement1;
statement1;  // (again)
16 голосов
/ 20 октября 2010

Если цикл слишком многословен для вас, вы также можете определить для него псевдоним:

#define TWICE for (int _index = 0; _index < 2; _index++)

Это приведет к следующему коду:

TWICE {
    // Do Stuff
}

// or

TWICE
    func();

Я бы порекомендовал использовать этот макрос только в том случае, если вам приходится делать это очень часто, я думаю, что простой цикл for более читабелен.

9 голосов
/ 20 октября 2010

К сожалению, это не для C, а только для C ++, но делает именно то, что вы хотите:

Просто включите заголовок, и вы можете написать что-то вроде этого:

10 times {
  // Do stuff
}

Я постараюсь переписать его и для Си.

8 голосов
/ 09 марта 2014

Итак, через некоторое время, вот подход, который позволяет вам написать следующее на чистом C:

2 times {
  do_something()
}

Пример:

Вы должны будете включить эту маленькую вещь в качестве простого заголовочного файла (я всегда называл файл extension.h). Тогда вы сможете писать программы в стиле:

#include<stdio.h>
#include"extension.h"

int main(int argc, char** argv){
    3 times printf("Hello.\n");
    3 times printf("Score: 0 : %d\n", _);
    2 times {
        printf("Counting: ");
        9 times printf("%d ", _);
        printf("\n");
    }
    5 times {
        printf("Counting up to %d: ", _);
        _ times printf("%d ", _);
        printf("\n");
    }
    return 0;
}

Особенности:

  • Простая запись простых петель (в стиле, изображенном выше)
  • Счетчик неявно хранится в переменной с именем _ (простое подчеркивание).
  • Разрешено вложение циклов.

Ограничения (и как (частично) их обойти):

  • Работает только для определенного числа циклов (что - «конечно» - разумно, так как вы хотели бы использовать такую ​​вещь только для «маленьких» циклов). Текущая реализация поддерживает максимум 18 итераций (более высокие значения приводят к неопределенному поведению). Можно изменить в заголовочном файле, изменив размер массива _A.
  • Допускается только определенная глубина вложения. Текущая реализация поддерживает глубину вложения 10. Может быть скорректирована путем переопределения макроса _Y.

Пояснение:

Здесь вы можете увидеть полный (= де-обфусцированный) исходный код . Допустим, мы хотим разрешить до 18 циклов.

  • Получение верхней границы итерации: Основная идея состоит в том, чтобы иметь массив char с, которые изначально все установлены в 0 (это массив counterarray). Если мы позвоним, например, 2 times {do_it;}, макрос times должен установить для второго элемента counterarray значение 1 (т.е. counterarray[2] = 1). В C можно поменять местами индекс и имя массива в таком присваивании, поэтому мы можем написать 2[counterarray] = 1 для достижения того же самого. Это именно то, что макрос times делает в качестве первого шага. Затем мы можем позже сканировать массив counterarray, пока не найдем элемент, который не равен 0, а равен 1. Тогда соответствующий индекс является верхней границей итерации. Хранится в переменной searcher. Поскольку мы хотим поддерживать вложение, мы должны хранить верхнюю границу для каждой глубины вложения отдельно, это делается с помощью searchermax[depth]=searcher+1.
  • Регулировка текущей глубины вложения: Как уже говорилось, мы хотим поддерживать вложение циклов, поэтому мы должны отслеживать текущую глубину вложения (выполняется в переменной depth). Мы увеличиваем его на единицу, если запускаем такой цикл.
  • Фактическая переменная счетчика: У нас есть «переменная» с именем _, которая неявно получает текущий счетчик. Фактически, мы храним один счетчик для каждой глубины вложения (все хранятся в массиве counter. Тогда _ - это просто еще один макрос, который извлекает правильный счетчик для текущей глубины вложения из этого массива.
  • Фактический цикл for: Мы берем цикл for на части:
    • Мы инициализируем счетчик для текущей глубины вложения до 0 (делается с помощью counter[depth] = 0).
    • Шаг итерации - самая сложная часть: мы должны проверить, достиг ли конец цикла на текущей глубине вложенности. Если это так, мы должны соответствующим образом обновить глубину вложенности. Если нет, мы должны увеличить текущий счетчик глубины вложения на 1. Переменная lastloop равна 1, если это последняя итерация, в противном случае - 0, и мы соответствующим образом корректируем текущую глубину вложения. Основная проблема здесь заключается в том, что мы должны записать это как последовательность выражений, разделенных запятыми, что требует от нас написания всех этих условий очень не простым способом.
    • «Шаг приращения» цикла for состоит только из одного присваивания, которое увеличивает соответствующий счетчик (то есть элемент counter правильной глубины вложения) и присваивает это значение нашей «переменной счетчика» _ .
4 голосов
/ 19 октября 2010

Что сказал абеленки.

А если ваш { // Do stuff } является многострочным, сделайте его функцией и вызовите эту функцию - дважды.

3 голосов
/ 30 июля 2011

Еще одна попытка:

for(i=2;i--;) /* Do stuff */

У этого решения есть много преимуществ:

  • Кратчайшая форма, я утверждаю (13 символов)
  • Тем не менее, читабельно
  • Включает инициализацию
  • Количество повторов ("2") видно в коде
  • Может использоваться в качестве переключателя (1 или 0) внутри тела, например, для чередования
  • Работает с одной инструкцией, телом инструкции или вызовом функции
  • Гибко (не нужно использовать только для "выполнения дважды")
  • Dijkstra совместимый; -)

Из комментария:

for (i=2; i--; "Do stuff");
3 голосов
/ 20 октября 2010

Как насчет этого?

void DostuffFunction(){}

for (unsigned i = 0; i < 2; ++i, DostuffFunction());

С уважением, Пабло.

2 голосов
/ 19 октября 2010

Используйте функцию:

func();
func();

Или используйте макрос (не рекомендуется):

#define DO_IT_TWICE(A) A; A

DO_IT_TWICE({ x+=cos(123); func(x); })
...