Будет работать следующий код:
#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)
#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
#define MYLIST(XX) \
DELAYED_CALL(XX, 1, hello) \
DELAYED_CALL(XX, 2, world)
int foo = EVAL2(MYLIST(AA)) 0;
Вывод: int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
К сожалению, у меня нет глубокого понимания , почему это работает; Я только что попробовал несколько трюков, которые, как правило, помогают в подобных случаях. Но я могу объяснить кое-что из этого.
Иногда макрос помечается как «не подлежащий дальнейшему расширению». Флаг обычно устанавливается, когда вы начинаете расширять его, и сбрасывается, когда расширение заканчивается. Это имеет тенденцию предотвращать рекурсию.
Когда макрос разворачивается в токены, которые обычно были бы похожими на функции вызовами макросов, иногда мы уже прошли этап, на котором они были бы расширены.
Мы можем обойти первую проблему, отложив расширение макроса до точки, где флаг не вызовет никаких проблем, добавив второй макрос в , создавая вызов макроса при его оценке. Вот что делает DELAYED_CALL
. Но при этом мы сталкиваемся со второй проблемой, поэтому нам нужно добавить несколько вызовов в EVAL
для повторного сканирования макроса (аргументы к функциональному макросу всегда сканируются, поэтому передается последовательность токенов в функциональный макрос, который просто выводит свои аргументы, вызывает повторное сканирование).
1117 Иногда нам нужно пару повторных проверок, чтобы все работало. EVAL2(X)
это просто сокращение от EVAL(EVAL(X))
. Иногда потребуется больше уловок.
Приведенный ниже код немного разъясняет, что происходит. Обратите внимание, что версии MYLIST2 требуется на один меньше EVAL; это потому, что AA вызывает MYLIST, а MYLIST2 - тот, у которого установлен флаг нарушителя.
#define EVAL(...) __VA_ARGS__
#define EVAL2(...) EVAL(__VA_ARGS__)
#define EVAL3(...) EVAL2(__VA_ARGS__)
#define EMPTY()
#define DELAYED_CALL(F, ...) F EMPTY()(__VA_ARGS__)
#define BB(i,n) i +
#define AA(i,n) i + (MYLIST(BB) 0) +
#define MYLIST(XX) \
DELAYED_CALL(XX, 1, hello) \
DELAYED_CALL(XX, 2, world)
#define MYLIST2(XX) \
XX(1, hello) \
XX(2, world)
% MYLIST
int foo = MYLIST(AA) 0;
int foo = EVAL(MYLIST(AA)) 0;
int foo = EVAL2(MYLIST(AA)) 0;
% MYLIST2
int foo = MYLIST2(AA) 0;
int foo = EVAL(MYLIST2(AA)) 0;
int foo = EVAL2(MYLIST2(AA)) 0;
Вывод этого будет:
% MYLIST
int foo = AA (1, hello) AA (2, world) 0;
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
% MYLIST2
int foo = 1 + (BB (1, hello) BB (2, world) 0) + 2 + (BB (1, hello) BB (2, world) 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
int foo = 1 + (1 + 2 + 0) + 2 + (1 + 2 + 0) + 0;
(Знаки% не являются чем-то особенным. Я просто хотел, чтобы комментарии отображались в выводе, а комментарии в стиле C удалить во время предварительной обработки.)
Дополнительная литература . Автор статьи понимает это гораздо лучше, чем я.
Первое является больше идиоматичный . В частности, это указывает (в смысле на основе 0) на количество повторений. При использовании чего-то на основе 1 (например, JDBC, IIRC) я мог бы испытать желание использовать < =. Так:
for (int i=0; i < count; i++) // For 0-based APIs
for (int i=1; i <= count; i++) // For 1-based APIs
я ожидал бы, что различие в производительности будет незначительно небольшим в реальном коде.
Не используйте магические числа.
, Почему это 7? (или 6 в этом отношении).
используют корректный символ для числа, которое Вы хотите использовать...
, В этом случае я думаю, что лучше использовать
for ( int i = 0; i < array.size(); i++ )
Создание привычки к использованию < сделает это последовательным и для Вас и для читателя, когда Вы выполните итерации через массив. Будет более просто для всех иметь стандартную конвенцию. И если Вы используете язык с массивами на основе 0, затем < конвенция.
Это почти наверняка имеет значение больше, чем какое-либо различие в производительности между < и < =. Стремитесь к функциональности и удобочитаемости сначала, затем оптимизируйте.
Другое примечание - то, что было бы лучше иметь привычку делать ++ я, а не я ++, так как выборка и инкремент требует временного файла и инкрементный, и выборка не делает. Для целых чисел Ваш компилятор, вероятно, оптимизирует временный файл далеко, но если Ваш тип итерации более сложен, он не смог.
Я не думаю, что существует различие в производительности. Вторая форма определенно более читаема, хотя, Вы не должны мысленно вычитать для нахождения последнего итеративного числа.
РЕДАКТИРОВАНИЕ: я вижу, что другие не соглашаются. Для меня лично, мне нравится видеть фактические индексы в циклической структуре. Возможно, это - потому что это более напоминает о Perl 0..6
синтаксис, который я знаю, эквивалентно (0,1,2,3,4,5,6)
. Если я вижу 7, я должен проверить оператор рядом с ним, чтобы видеть, что на самом деле индекс 7 никогда не достигается.
Замеченный с точки зрения оптимизации это не имеет значения.
Замеченный с точки зрения стиля кода я предпочитаю <. причина:
for ( int i = 0; i < array.size(); i++ )
настолько более читаемо, чем
for ( int i = 0; i <= array.size() -1; i++ )
также < дает Вам количество повторений немедленно.
Другое голосование за < это, Вы могли бы предотвратить много случайных ошибок off-one.
Я всегда использую < array.length, потому что легче читать, чем < = array.length-1.
также наличие < 7 и, учитывая, что Вы знаете, что это запускается с 0 индексов, это должно быть интуитивно, что число является количеством повторений.
Я предпочитаю:
for (int i = 0; i < 7; i++)
я думаю, что это переводит с большей готовностью в "итерацию через цикл 7 раз".
я не уверен в последствиях производительности - я подозреваю, что любые различия скомпилировать далеко.
Это не имеет никакого эффективного значения когда дело доходит до производительности. Поэтому я использовал бы, какой бы ни легче понять в контексте проблемы, которую Вы решаете.
@Chris, Ваш оператор о.Length том, чтобы быть дорогостоящим в.NET на самом деле неверен и в случае простых типов полная противоположность.
int len = somearray.Length;
for(i = 0; i < len; i++)
{
somearray[i].something();
}
на самом деле медленнее, чем
for(i = 0; i < somearray.Length; i++)
{
somearray[i].something();
}
, позже случай, который оптимизирован временем выполнения. Так как время выполнения может гарантировать, что я - допустимый индекс в массив, никакие граничные проверки не сделаны. В первом время выполнения не может гарантировать, что я не был изменен до цикла и вызываю граничные проверки на массиве для каждого индексного поиска.
Я всегда предпочитал:
for ( int count = 7 ; count > 0 ; -- count )
Я сказал бы что использование "< 7-дюймовая версия, потому что это - то, что большинство людей считает - поэтому, если люди будут, просматривает чтение Вашего кода, они могли бы интерпретировать его неправильно.
я не волновался бы о ли "<"; более быстро, чем "< =", просто пойдите для удобочитаемости.
, Если Вы действительно хотите пойти для увеличения скорости, рассмотрите следующее:
for (int i = 0; i < this->GetCount(); i++)
{
// Do something
}
Для увеличения производительности можно немного перестроить его к:
const int count = this->GetCount();
for (int i = 0; i < count; ++i)
{
// Do something
}
Уведомление удаление GetCount () от цикла (потому что это будет запрошено в каждом цикле), и изменение "меня ++" к "++ я".
В Java 1.5 можно просто сделать
for (int i: myArray) {
...
}
так для случая массива, который Вы не должны волновать.
Я помню со своих дней, когда мы сделали 8 086 блоков в колледже, это было более производительно, чтобы сделать:
for (int i = 6; i > -1; i--)
как был операция JNS , которая означает Переход если Никакой Знак. Используя это означал, что не было никакого поиска памяти после каждого цикла, чтобы получить сравнительное значение и не выдержать сравнение также. В эти дни большинство компиляторов оптимизирует использование регистра, таким образом, вещь памяти больше не важна, но Вы все еще добираетесь, необязательное выдерживают сравнение.
способом поместить 7 или 6 в Вашем цикле представляет" магическое число ". Для лучшей удобочитаемости необходимо использовать константу с Поглощенным Разоблачающим Именем. Как это:
const int NUMBER_OF_CARS = 7;
for (int i = 0; i < NUMBER_OF_CARS; i++)
РЕДАКТИРОВАНИЕ: Люди aren’t получение вещи блока так более полный пример, очевидно, требуются:
, Если мы делаем для (я = 0; я < = 10; я ++), необходимо сделать это:
mov esi, 0
loopStartLabel:
; Do some stuff
inc esi
; Note cmp command on next line
cmp esi, 10
jle exitLoopLabel
jmp loopStartLabel
exitLoopLabel:
, Если мы делаем для (интервал i = 10; i>-1; я-), затем можно сойти с рук это:
mov esi, 10
loopStartLabel:
; Do some stuff
dec esi
; Note no cmp command on next line
jns exitLoopLabel
jmp loopStartLabel
exitLoopLabel:
я просто проверил, и компилятор C++ Microsoft не делает этой оптимизации, но это делает, если Вы делаете:
for (int i = 10; i >= 0; i--)
, Таким образом, мораль - то, если Вы используете Microsoft C ++ вЂ, и возрастаете или убываете, не имеет никакого значения, для получения быстрого цикла, который необходимо использовать:
for (int i = 10; i >= 0; i--)
, а не любой из них:
for (int i = 10; i > -1; i--)
for (int i = 0; i <= 10; i++)
, Но откровенно получение удобочитаемости "для (интервал i = 0; я < = 10; я ++)", обычно намного более важно, чем пропавшие без вести одной команды процессора.
†Другие компиляторы могут сделать разные вещи.
Во-первых, не используйте 6 или 7.
Лучше для использования:
int numberOfDays = 7;
for (int day = 0; day < numberOfDays ; day++){
}
В этом случае это лучше, чем использование
for (int day = 0; day <= numberOfDays - 1; day++){
}
Еще лучше (Java / C#):
for(int day = 0; day < dayArray.Length; i++){
}
И еще лучше (C#)
foreach (int day in days){// day : days in Java
}
обратный цикл действительно быстрее, но так как более трудно читать (если не Вами другими программистами), лучше избежать в. Особенно в C#, Java...
Я соглашаюсь с толпой, заявляющей, что эти 7 имеют смысл в этом случае, но я добавил бы, что в случае, где эти 6 важно, скажите, что Вы хотите ясно дать понять, что Вы только действуете на объекты до 6-го индекса, затем < = лучше, так как это делает 6 легче видеть.
Edsger Dijkstra записал статью об этом назад в 1982, где он приводит доводы ниже < = я < верхний:
существует самое маленькое натуральное число. Исключение нижней границы — как в b) и d) — вызывает для подпоследовательности, запускающейся в самом маленьком натуральном числе нижняя граница, как упомянуто в область неестественных чисел. Это ужасно, таким образом, для нижней границы мы предпочитаем в‰ ¤ как в a) и c). Рассмотрите теперь подпоследовательности, запускающиеся в самом маленьком натуральном числе: включение верхней границы затем вынудило бы последнего быть неестественным к тому времени, когда последовательность уменьшилась к пустой. Это ужасно, таким образом, для верхней границы мы предпочитаем < как в a) и d). Мы приходим к заключению, что конвенция a) состоит в том, чтобы быть предпочтена.
Это непосредственно подпадает под категорию "Взгляд Неверного кода Создания Неправильно" .
На основанных на нуле языках индексирования, таких как Java или люди C# приучены к вариациям на index < count
условие. Таким образом усиление этой defacto конвенции сделало бы ошибки диапазона более очевидными.
Относительно производительности: любой хороший компилятор, стоящий его объема потребляемой памяти, должен представить, такие как надуманный вопрос.
Путь назад в колледже, я помню, что что-то об этих двух операциях, являющихся подобным в, вычисляет время на ЦП. Конечно, мы перекрикиваем на уровне ассемблера.
Однако, если Вы говорите C# или Java, я действительно не думаю, что каждый будет повышением скорости по другому, Несколько наносекунд, которые Вы получаете, наиболее вероятны не стоящие любого беспорядка, который Вы представляете.
Лично, я создал бы код, который имеет смысл с бизнес-точки зрения реализации, и удостоверьтесь, что легко читать.
Как небольшое в стороне, когда цикличное выполнение через массив или другой набор в .NET, я нахожу
foreach (string item in myarray)
{
System.Console.WriteLine(item);
}
быть более читаемым, чем числовое для цикла. Это, конечно, предполагает, что сам фактический встречный Интервал не используется в коде цикла. Я не знаю, существует ли изменение производительности.
Существует много серьезных оснований для записи i< 7. Наличие номера 7 в цикле, который выполняет итерации 7 раз, хорошо. Производительность эффективно идентична. Почти все пишут i< 7. Если Вы пишете для удобочитаемости, используйте форму, которую все распознают немедленно.
В C++ я предпочитаю использовать !=
, который применим со всеми контейнерами STL. Не все итераторы контейнера STL меньше сопоставимы.
Оба из тех циклов выполняют итерации 7 раз. Я сказал бы, что тот с 7 в нем более читаем/более ясен, если у Вас нет действительно серьезного основания для другого.
Как люди заметили, нет никакого различия ни в одной из этих двух альтернатив, которые Вы упомянули. Только для подтверждения этого я сделал некоторое простое сравнительное тестирование в JavaScript.
Вы видите результаты здесь . То, что не ясно из этого, - то, что, если я подкачиваю положение 1-х и 2-х тестов, результатов для тех 2 тестов подкачка, это - ясно проблема памяти. Однако 3-й тест, тот, где я инвертирую порядок повторения, ясно быстрее.
Как все говорят, это обычно для использования 0-индексируемых итераторов даже для вещей за пределами массивов. Если все начинается в 0
и заканчивается в n-1
, и нижние границы всегда <=
, и верхние границы всегда <
, существует так намного меньше взглядов, что необходимо сделать при рассмотрении кода.
Вы могли также использовать !=
вместо этого. Тем путем Вы получите бесконечный цикл, если Вы совершите ошибку в инициализации, заставляя ошибку быть замеченными ранее и какие-либо проблемы, это вызывает, чтобы быть ограниченным застреванием в цикле (вместо того, чтобы иметь проблему намного позже и не найти его).
Строго с логической точки зрения, необходимо думать, что < count
было бы более эффективным, чем <= count
по точной причине, которая <=
будет тестировать на равенство также.
Я думаю или в порядке, но когда Вы выбрали, придерживайтесь один или другой. Если Вы привыкли использовать < =, затем попытайтесь не использовать < и наоборот.
я предпочитаю < =, но в ситуациях, где Вы работаете с индексами, которые запускаются в нуле, я, вероятно, попытался бы использовать <. это - все персональное предпочтение все же.
'<'; и '< =', операторы являются точно той же стоимостью производительности.
'<'; оператор является стандартным и более легким для чтения в основанном на нуле цикле.
Используя ++ я вместо я ++ улучшаю производительность в C++, но не в C# - я не знаю о Java.
Отличный вопрос. Мой ответ: используйте тип A ('<')
I , а не Индекс последнего элемента Так что равномерность важно.
Другая проблема со всей всей конструкцией. I
появляется 3 раза в нем, поэтому он может быть неправильно. Конструкция для петли говорит Как сделать вместо Что делать . Я предлагаю принимать это:
Boost_Foreach (I, IntegerInterInterval (0,7))
Это более четкое, компилируется для Exaclty одинаковых инструкций ASM и т. Д. Спросите меня код InteGerInterVal, если хотите.
Для некоторых языков/технологий как.NET с помощью.Size или.Length или размер () / длина () плохая идея, это доступы, что свойство каждый раз, когда это выполняет итерации, так присвоил его переменной, имеет немного меньше хита производительности.