Ответ 1
Как насчет этого?
void DostuffFunction(){}
for (unsigned i = 0; i < 2; ++i, DostuffFunction());
С уважением, Пабло.
Много раз мне нужно делать вещи TWICE в цикле for. Просто я могу настроить цикл for с помощью итератора и пройти его дважды:
for (i = 0; i < 2; i++)
{
// Do stuff
}
Теперь я заинтересован в том, чтобы делать это как ПРОСТО, как я могу, возможно, без инициализатора или итератора? Существуют ли другие, действительно простые и изящные способы достижения этого?
Как насчет этого?
void DostuffFunction(){}
for (unsigned i = 0; i < 2; ++i, DostuffFunction());
С уважением, Пабло.
Это элегантно, потому что он выглядит как треугольник; и треугольники элегантны.
i = 0;
here: dostuff();
i++; if ( i == 1 ) goto here;
Инкапсулируйте его в функцию и вызовите дважды.
void do_stuff() {
// Do Stuff
}
// .....
do_stuff();
do_stuff();
Примечание:, если вы используете переменные или параметры встроенной функции в логике элементов, вы можете передать их в качестве аргументов извлечен do_stuff
.
Если его только два раза, и вы хотите избежать цикла, просто напишите два слова.
statement1;
statement1; // (again)
Если цикл слишком подробный для вас, вы также можете определить его псевдоним:
#define TWICE for (int _index = 0; _index < 2; _index++)
Это приведет к тому, что код:
TWICE {
// Do Stuff
}
// or
TWICE
func();
Я бы рекомендовал использовать этот макрос, если вам нужно делать это очень часто, я думаю, что простой для цикла более читабельный.
К сожалению, это не для C, а только для С++, но делает именно то, что вы хотите:
Просто включите заголовок, и вы можете написать что-то вроде этого:
10 times {
// Do stuff
}
Я попробую переписать его и для C.
Итак, через какое-то время, вот такой подход, который позволяет вам написать следующее в чистом 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;
}
_
(простое подчеркивание)._A
._Y
.Здесь вы можете увидеть полный (= de-obfuscated) исходный код. Скажем, мы хотим разрешить до 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
. Затем _
- это просто еще один макрос, который извлекает правильный счетчик для текущей глубины вложенности из этого массива.counter[depth] = 0
).lastloop
равна 1, если это последняя итерация, иначе 0, и соответственно корректируем текущую глубину вложенности. Основная проблема здесь заключается в том, что мы должны написать это как последовательность выражений, разделенных запятыми, что требует от нас писать все эти условия очень не прямолинейным способом.counter
правильной глубины вложенности) и присваивает это значение нашей "счетной переменной" _
.Что сказал Абеленки.
И если ваш { // Do stuff }
является многострочным, сделайте его функцией и дважды вызовите эту функцию.
Другая попытка:
for(i=2;i--;) /* Do stuff */
Это решение имеет много преимуществ:
Из комментария:
for (i=2; i--; "Do stuff");
Использовать функцию:
func();
func();
Или используйте макрос (не рекомендуется):
#define DO_IT_TWICE(A) A; A
DO_IT_TWICE({ x+=cos(123); func(x); })
Если ваш компилятор поддерживает это, просто поместите объявление внутри оператора for
:
for (unsigned i = 0; i < 2; ++i)
{
// Do stuff
}
Это так же изящно и эффективно, как может быть. Современные компиляторы могут делать разворот цикла и все такое, доверять им. Если вы им не доверяете, проверьте ассемблер.
И это имеет одно небольшое преимущество для всех других решений, поскольку все, что он просто читает, "делают это дважды".
Многие люди предлагают написать код дважды, и это хорошо, если код короток. Существует, однако, размер блока кода, который было бы неудобно копировать, но недостаточно велико, чтобы заслужить его собственную функцию (особенно если этой функции потребуется чрезмерное количество параметров). Моя собственная нормальная идиома для запуска цикла 'n' times -
i = number_of_reps; do { ... whatever } while(--i);
В какой-то мере потому, что я часто кодирую встроенную систему, где цикл подсчета часто недостаточно эффективен, а в какой-то мере потому, что легко видеть количество повторений. Выполнение вещей дважды немного неудобно, потому что наиболее эффективное кодирование в моей целевой системе
bit rep_flag; rep_flag = 0; do { ... } while(rep_flag ^= 1); /* Note: if loop runs to completion, leaves rep_flag clear */
не очень хорошо читается. Используя числовой счетчик, количество повторений может варьироваться произвольно, что во многих случаях не будет иметь место. Тем не менее, числовой счетчик, вероятно, лучший выбор.
Предполагая поддержку С++ 0x lambda:
template <typename T> void twice(T t)
{
t();
t();
}
twice([](){ /*insert code here*/ });
Или:
twice([]()
{
/*insert code here*/
});
Это не поможет вам, поскольку вы хотели его для C.
Как Эдсгер У. Дейкстра сам сказал: "два или более, используйте для". Не нужно быть проще.
Хорошее правило: три или более, сделайте для.
Я думаю, что прочитал, что в Code Complete, но я мог ошибаться. Поэтому в вашем случае вам не нужен цикл for.
Это минимально возможное без трюков препроцессора/шаблона/дублирования:
for(int i=2; i--; ) /*do stuff*/;
Обратите внимание, что декремент происходит один раз в начале, поэтому он будет зацикливаться ровно дважды с индексами 1
и 0
в соответствии с запросом.
В качестве альтернативы вы можете написать
for(int i=2; i--; /*do stuff*/) ;
Но это чисто отличие вкуса.
Если то, что вы делаете, несколько сложно обернуть его в функцию и вызвать эту функцию дважды? (Это зависит от того, сколько локальных переменных использует ваш код do stuff
).
Вы можете сделать что-то вроде
void do_stuff(int i){
// do stuff
}
do_stuff(0);
do_stuff(1);
Но это может стать крайне уродливым, если вы работаете над целым рядом локальных переменных.
jump инструкция довольно медленная, поэтому, если вы пишете строки один за другим, она будет работать быстрее, чем писать цикл. но современные компиляторы очень, очень умны, и оптимизация отличная (если они разрешены, конечно). если вы включили оптимизацию вашего компилятора, вам все равно, вы пишете его - с циклом или нет (:
EDIT: http://en.wikipedia.org/wiki/compiler_optimizations просто взгляните (:
//dostuff
stuff;
//dostuff (Attention I am doing the same stuff for the :**2nd** time)
stuff;
Во-первых, используйте комментарий
/* Do the following stuff twice */
то,
1) используйте цикл for
2) напишите выражение дважды, или
3) напишите функцию и вызовите функцию дважды
не используйте макросы, как было сказано ранее, макросы являются злыми.
(Мой ответ почти треугольник)
Что такое элегантность? Как вы его измеряете? Кто-то платит вам за элегантность? Если да, то как они определяют преобразование доллара в элегантность?
Когда я спрашиваю себя: "Как это должно быть написано", я считаю приоритеты человека, который платит мне. Если мне платят, чтобы написать быстрый код, control-c, control-v, done. Если мне платят, чтобы написать код быстро, хорошо.. то же самое. Если мне платят за код, занимающий наименьшее количество места на экране, я сокращаю запас своего работодателя.
Вблизи вашего примера, элегантный и эффективный:
for (i = 2; i; --i)
{
/* Do stuff */
}
Вот почему я рекомендую этот подход:
Если "Do stuff" является длинным, переместите его в функцию и дайте компилятору разрешить его, если это выгодно. Затем вызовите функцию из цикла for.
void loopTwice (bool first = true)
{
// Recursion is your friend
if (first) {loopTwice(false);}
// Do Stuff
...
}
Я уверен, что там более элегантный способ, но это просто читать и просто писать. Там может быть даже способ устранить параметр bool, но это то, к чему я пришел через 20 секунд.