[c++] возможно ли произвольное число циклов без рекурсии?
#include <stdlib.h>
#include <stdio.h>
int main (int argc, char **argv) {
int r, l, i;
int *a;
l = atoi(argv[1]);
r = atoi(argv[2]);
a = calloc(l, sizeof(int;
next:
for (i = 0; i < l; i++)
printf("%d ", a[i]);
printf("\n");
for (i = 0; i < l; i++) {
if (++a[i] < r)
goto next;
a[i] = 0;
}
return 0;
}
Надо k вложенных цикловпростой массив счетчиков использовать можно?
void main
{
int k = 10;
int m = 100;
int counters[k+1]; //ячейка counters[k] используется для маркировки признака конца
for (begin(counters, k);is_next(counters, k);next(counters,k, m
{
...
}
}
void begin(int* counters, int k)
{
for (int i = 0; i < k+1; ++i)
counters[i] = 0;
}
void next(int * counters, int k, int m)
{
for (int i = 0; i < k; ++i)
{
counters[i]++;
if (counters[i] < m)
return;
counters[i] = 0;
}
counters[k] = 1;
}
bool is_next(int * counters, int k)
{
return counters[k] == 0;
}

bool next(int* counters, int k, int m) {
for (int i = 0; i < k; ++i)
{
if (++counters[i] < m)
return true;
counters[i] = 0;
}
return false;
}
как-то так
как-то такя об этом думал, но тогда цикл снаружи придется делать менее "красивым":
for (begin(counters, k);)
{
...
if(!next(counters, k, m
break;
}
ps
а делать изменение счетчика из под проверки условия, имхо, не кошерно
void main
{
int k = 10;
int m = 100;
int counters[k];
bool is_continue = true;
for (begin(counters, k); is_continue; is_continue = next(counters,k, m
{
...
}
}
void begin(int* counters, int k)
{
for (int i = 0; i < k; ++i)
counters[i] = 0;
}
bool next(int * counters, int k, int m)
{
for (int i = 0; i < k; ++i)
{
counters[i]++;
if (counters[i] < m)
return true;
counters[i] = 0;
}
return false;
}
NOooooooooooooooooooooo
void main
{
int k = 10;
int m = 100;
int counters[k];
for (bool is_continue = begin(counters, k); is_continue; is_continue = next(counters,k, m
{
...
}
}
bool begin(int* counters, int k)
{
for (int i = 0; i < k; ++i)
counters[i] = 0;
return true;
}
bool next(int * counters, int k, int m)
{
for (int i = 0; i < k; ++i)
{
counters[i]++;
if (counters[i] < m)
return true;
counters[i] = 0;
}
return false;
}
почему нет?
о боже, я не могу. Т.е. ты добавил в функцию return true только чтобы присвоить более лакончино значение переменной? Тогда уж сделал бы хотя бы return false если k = 0 (на самом деле еще лучше если k = 0 или m = 0).
о боже, я не могу.ничего ты не понимаешь в красоте
Тогда уж сделал бы хотя бы return false если k = 0 (на самом деле еще лучше если k = 0 или m = 0).это само собой
ps
а чем тебе начальный вариант с is_continue не понравился?
Можно и вообще без подпрограмм писать в таком случае.
---
"Нахер нужны выпускники мехмата, если они за третий класс
математику не знают... Мозг высшей геометрией разрушен."
я об этом думал, но тогда цикл снаружи придется делать менее "красивым":use do-while, Luke
use do-while, Lukeне красиво. задание начальных условий оторвано от цикла
for( init; isValid; advance ) {
doSmth;
}
либо если у тебя одна прибаблялка, тогда уж
init;
do
{
doSmth;
} while( tryAdvance );
т.е. зачем скрывать, что у тебя там одна итерация в любом случае выполняется?
С одной прибавлялкой полюбому нельзя проверить валидность итератора.
Эта идиома (твой цикл for) приобретает смысл только во втором случае: заходим в цикл при опредленных условиях (begin вернул true и еще проверяем условие в конце каждой итерации. Через другие циклы это вроде лакончино не запишешь. И формально этот is_continue (оставим лингвистику в стороне, но имя тоже ппц

ничего ты не понимаешь в красотео, я теперь не буду делать функции void вообще, буду везде делать return true, вдруг заодно придется присвоить какой-нибудь перменной true, можно будет сократить код

ps
контракт не обязательно должен оформляться в виде реализации ооп-interface-а
т.е. зачем скрывать, что у тебя там одна итерация в любом случае выполняется?второй вариант как раз получается полным, там такого уже нет, и контракт позволяет корректно обработать и вырожденные случаи
у тебя просто везде попытка приклеить isValid либо к одной, либо к другой функции. Это меньше писанины, но интерфейс не упрощает.
Это меньше писанины, но интерфейс не упрощает.менее наглядный, но также и менее вычислительно-затратный, чем стандартный c++-контракт: begin, end, ++, сравнение
у тебя просто везде попытка приклеить isValid либо к одной, либо к другой функции.закономерный перенос. Во многих случаях функции begin, next уже имеют внутреннее знание (как в данном случае) о необходимости продолжения цикла, соответственно, явный вызов функции isValid приносит лишь дополнительные накладные расходы
ну как бэ плевать на доп. расходы, преждевременная оптимизация же, затрудняющая общение с итератором. В конце концов это итератор, на одно приращение итератора чаще всего приходится куча действий по выполнению самой итерации.
преждевременная оптимизация же, затрудняющая общение с итератором.это утверждание подразумевает, что когда-то делается окончательная оптимизация, но в реальности никакой окончательной оптимизации не происходит.
В конце концов это итератор, на одно приращение итератора чаще всего приходится куча действий по выполнению самой итерации.какой на твой взгляд оверхед не надо оптимизировать? 80%? 20%? 5%? 1%? 0.1%?
ps
замечу, что enumerable из foreach - это дальнейшее развитие предложенного контракта.
enumerable_begin(ref state)
{
state = специальное_начальное_состояние;
}
enumerable_next(ref state)
{
if (state == специальное_начальное_состояние)
return begin(ref state);
return next(ref state);
}
enumerable_current(state)
{
return state;
}
минус у enumerable-контракта в том, что он также увеличивает затраты по памяти и вычислениям из-за необходимости введения специального начального состояния, и его отслеживанию.
это утверждание подразумевает, что когда-то делается окончательная оптимизация, но в реальности никакой окончательной оптимизации не происходит.не знаю, о какой реальности ты говоришь, но обычно если важна производительность, то перед релизом юзают профилировщик, и что-то я не замечал, чтобы итерирование было узким местом.
какой на твой взгляд оверхед не надо оптимизировать? 80%? 20%? 5%? 1%? 0.1%?это проценты от чего? Если от времени выполнения всего алгоритма, то из-за 5% в большинстве случаев заморачиваться бы не стал (опять же, бывают исключения но что это за программа, которая на 20% состоит из приращения итераторов? Может быть, ее адеватнее написать без всяких излишеств с итераторами, а просто как-нибудь аццки быстро?
то перед релизом юзают профилировщикпрофилировщик не является серебренной пулей, он показывает лишь малую часть проблем с производительностью в коде.
но что это за программа, которая на 20% состоит из приращения итератороввсе программы по обработке информации, а это почти все инет-сервисы и бизнес приложения.
все программы по обработке информации, а это почти все инет-сервисы и бизнес приложения.Давай-ка пример конкретный, потому что по моему опыту все "программы по обработке информации" упираются в медленный доступ к памяти, а не в итераторы.
профилировщик не является серебренной пулей, он показывает лишь малую часть проблем с производительностью в коде.но узкое место в коде приращения итератора должен заметить, думаешь нет?
Если этот код заинлайнится в сотню мест - нет.

но узкое место в коде приращения итератора должен заметить, думаешь нет?common-профайлер тебе не скажет смог ли у тебя цикл влезть в кэш(и по коду, и по данным а это меняет производительность в несколько раз.
Давай-ка пример конкретный, потому что по моему опыту все "программы по обработке информации" упираются в медленный доступ к памяти, а не в итераторы.если узкое место память, то и становится важно насколько у тебя оптимизирован итератор, и насколько он не создает лишних обращений к памяти. Также в отличии от вычислительных задач, в данных задачах тело итераций дешевое (несколько сравнений или присваиваний что при решении задач оптимизации возвращает нас к качеству кода самого итератора.
common-профайлер тебе не скажет смог ли у тебя цикл влезть в кэш(и по коду, и по данным а это меняет производительность в несколько раз.Я не понимаю, зачем ты пишешь эти верные утверждения?
Вообще, эта проблема обсуждалась уже много раз и единственное, что я знаю, так это то, что медленный код редко бывал проблемой в моем опыте, обычно это решается оптимизацией. А вот замороченный и непонятный код, который на самом деле не быстрее, чем написанное по-простому, мне встречался слишком часто, чтобы пропагандировать эту логику. Вообще, в оптимизации бизнес-приложений у меня не слишком обширный опыт, но вот кода я начитался нормально и замечал корелляцию общего качества и понятности кода с этими оптимизациями.
если узкое место память, то и становится важно насколько у тебя оптимизирован итератор, и насколько он не создает лишних обращений к памяти. Также в отличии от вычислительных задач, в данных задачах тело итераций дешевое (несколько сравнений или присваиваний что при решении задач оптимизации возвращает нас к качеству кода самого итератора.по моему опыту, это неверно в 90% случаев (обработка графов, разреженные матрицы). Дело в том, что даже такой большой итератор, как в обсуждаемой теме, легко помещается в кэш (а еше чаще в регистры, если итераторы более простые, а их большинство а тормозит как раз произвольный доступ к памяти. Поэтому я и попросил более конкретный пример, потому что то, о чем ты говоришь, для меня новость. Нет, я могу придумать, например, умножение плотных матриц или еще какие-нибудь последовательные паттерны доступа, но и здесь если там floating point, то все это может скрыться в конвейере.
давай начнем с простого вопроса, как бы ты оформил итератор из исходного поста?
for( Counter c = Counter(k); c.IsValid; c++ ) {
// Тут c[i] дает значение соответствующего разряда.
}
class Counter {
public:
Counter(int nestedIterations, int iterationLength_) :
iterationLength(iterationLength_
data(nestedIterations + 1)
{}
bool IsValid const
{
return data.back == 0;
}
Counter& operator++
{
Assert(IsValid;
for( int i = 0; i < data.size - 1; i++ ) {
data[i]++;
if( data[i] < iterationLength ) {
return *this;
}
data[i] = 0;
}
data.back = 1;
return *this;
}
Counter operator++(int) const
{
Counter old(*this);
++(*this);
return old;
}
int operator[](int i) const
{
return data[i];
}
private:
int iterationLength;
std::vector<int> data;
};
И, кстати, как-то так автоматически получилось, что можно заоптимизировать IsValid: закэшировать это в поле класса и менять его в конструкторе и функции Next.если закэшировать IsValid, то это и будет один в один вариант с предложенным, только у тебя он в ООП-обертке.
при таком итераторе на оверхед уйдут 20-50% производительностину это же полностью некорректное утверждение, ибо сильно зависит от тела цикла и как минимум в меньшую сторону легко можно выйти (а в большую и не в моих интересах пытаться).
еще не указано, оверхед по сравнению с чем?
если закэшировать IsValid, то это и будет один в один вариант с предложенным, только у тебя он в ООП-обертке.Да, но у меня сохранится последовательность и понятность интерфейса и это не приведет к модификации кода. Вообще, мы тут не ООП обсуждаем, это я написал просто чтобы полностью ответить на твой вопрос "как бы я...".
если у тебя отсутствует задача писать максимально быстрый код, то обсуждать в целом нечего и неинтересно.
Откуда такие выводы? Я заинтересован в написании быстрого кода, ок? Но это не отменяет того, что твое утверждение про %% не имеет смысла в данном виде по двум причинам, которые я написал.
про кэшировние, кстати, я бред написал (там была неправильная реализация IsValid, затупил). Сейчас подредактировал.
А у тебя эта задача присутствует?
Когда это ты начал программировать медленные микроконтроллеры?
---
"А что такое line rate? Заодно обоснуй это мнение."
Когда это ты начал программировать медленные микроконтроллеры?лет с 13-ти
Сейчас подредактировал.этот вариант уже с ошибкой (как и у меня первый вариант он неправильно обрабатывает вырожденный случай, когда итерировать нечего.
Я заинтересован в написании быстрого кода, ок?ты же сам сказал, что у тебя задача другая - писать быстрый код только в тех случаях, когда это заказывают.
using namespace std;
class Counter {
public:
Counter(int nestedIterations, int iterationLength_) :
iterationLength(iterationLength_
data(nestedIterations + 1)
{}
bool IsValid const
{
return data.back == 0;
}
Counter& operator++
{
//Assert(IsValid;
for( int i = 0; i < data.size - 1; i++ ) {
data[i]++;
if( data[i] < iterationLength ) {
return *this;
}
data[i] = 0;
}
data.back = 1;
return *this;
}
Counter operator++(int)
{
Counter old(*this);
++(*this);
return old;
}
int operator[](int i) const
{
return data[i];
}
private:
int iterationLength;
std::vector<int> data;
};
const int k = 10;
const int m = 10;
void test1
{
int sum = 0;
for( Counter c = Counter(k, m); c.IsValid; ++c ) {
for( int i = 0; i < k; i++ ) {
sum += c[i];
}
}
cout << sum << endl;
}
bool begin(int* counters, int k)
{
for (int i = 0; i < k; ++i)
counters[i] = 0;
return true;
}
bool next(int * counters, int k, int m)
{
for (int i = 0; i < k; ++i)
{
counters[i]++;
if (counters[i] < m)
return true;
counters[i] = 0;
}
return false;
}
void test2
{
int counters[k];
int sum = 0;
for (bool is_continue = begin(counters, k); is_continue; is_continue = next(counters,k, m
{
for( int i = 0; i < k; i++ ) {
sum += counters[i];
}
}
cout << sum << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
double start, end;
start = getCPUTime;
test1;
end = getCPUTime;
cout << end-start << endl;
start = getCPUTime;
test2;
end = getCPUTime;
cout << end-start << endl;
return 0;
}
getCPUtime основано на GetProcessTimes. Core i5. Студия 2010, все оптимизации врублены.
-971566080
25.506
-971566080
24.227
Это практически 5% производительности.
При несколько други параметрах (5^14) получается побольше, тут ~15%:
-900254340
34.992
-900254340
29.92
И это только самый тривиальный код: он не обращается к посторонней памяти, просто суммирует показания итератора, все ложится в кэш. Добавь тут обращения к внешней памяти, чтобы из кэша вылезло, и там пару процентов сложно будет набрать.
да, действительно, но это не так важно: как минимум это исправляется одним if'ом в конструкторе, может быть можно и поизящнее.
ты же сам сказал, что у тебя задача другая - писать быстрый код только в тех случаях, когда это заказывают.я так не говорил. Я говорил, что не хочу жертвовать читабельностью ради ускорения в 5%.
Это практически 5% производительности.это потери чисто на некорректно выбранную объектную обертку, а измерь еще некэшированный вызов IsValid (про что и утверждалось, что он вносит существенные тормоза)
При несколько други параметрах получается побольше, тут ~15%:
Вообще, с точки зрения производительности твои два способа отличаются только тем, где хранить is_continue: в отдельной перменной или в конце массива

Тред не осилил, простите если уже было

да, круто, только неясно, зачем swich, если это как бэ просто параметр шаблона.
Вообще, мой поинт в том, что итераторы вообще можно как угодно писать, ООП-не ООП, главное в питон не интеропить, на производительности в сколько-нибудь реальных приложениях это скажется копеечно, поэтому тут в первую очередь стоит думать о читабельности/интерфейсе, а не микрооптимизациями заниматься.
критичных по производительности, чем то, что клепает ,
цена вменяемого интерфейса значительно выше, чем цена
микрооптимизации.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."
достаточно часто, невозможность сделать хороший интерфейс упирается в отсутствие запаса по производительности.
в отсутствие запаса по производительности, почти во всех моих
случаях, упирались в недостаток времени на переработку кода.
---
"Vyroba umelych lidi, slecno, je tovarni tajemstvi."
некорректно выбранную объектную оберткучто значит некорретно выбранную? Зачем вообще писать на плюсах в си-стиле? Тогда можно писать на Си.
что значит некорретно выбранную?в твоем коде более тесно связаны текущий вариант и пространство вариантов, чем текущий вариант и действия по его обработке.
в твоем коде более тесно связаны текущий вариант и пространство вариантов, чем текущий вариант и действия по его обработке.вот читаю это и даже забыв, что сам написал, думаю, что "все правильно сделал".
код построения пространства вариантов от задачи к задачи обычно не меняется, а вот вид состояния в каждой задаче уникален.
у тебя же состояние задачи и код построения пространства вариантов смешаны в одном классе
да, круто, только неясно, зачем swich, если это как бэ просто параметр шаблона.run-time -> compile-time

То, что ты не сильно заботишься о читабельности, видно хотя бы из отношения к именованию функций.
что у меня будет на 20-50% медленее, что оказалось в самом вырожденном случае пургой.ты же код в сообщении пост фактум поправил..
а, ты про тот цикл. Он вообще не в кассу был, я там тупанул, оно вообще не работало в том виде. В любом случае, я и с той реализацией могу привести пример, где будет меньше 20% разница (например, внутри цикла sleep зафигачить так что это уже мелочи: главная мысль тут не в конкретных цифрах, а в том, что не стоит преждевременно оптимизировать код под производительность.
не стоит преждевременно оптимизировать код под производительность.ты хотя бы правильные утверждения приводи:
не стоит преждевременно в ущерб читабельности оптимизировать код под производительность

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

Но ведь раньше шел разговор о скорости, по-моему, тут достаточно явно продемонстрировано, что на скорость оформление итератора повлиять не может, даже если нагородить ООП.вот только заметь что для оформления итератора ты почему-то взял не стандартную для C++ (из stl) идиому оформления итераторов, а придумал какую-то свою.

Можно было и из STL'я взять, там даже random access несложно реализовать. Просто почему-то я исторически не фан STL'я, не считаю его за аксиому.
Можно было и из STL'я взятьпокажи, пожалуйста, такой вариант и его производительность.
ps
оценку скорости разработки тоже желательно указать
Если хочется C++, то вот такое решение достаточно чистое без лишних напластований кода, которые постановкой задачи еще не требуются.
void main
{
int m = 100;
std::vector<int> counters(10);
for (bool is_continue = begin(counters, m); is_continue; is_continue = next(counters,m
{
...
}
}
bool begin(std::vector<int>& counters, int m)
{
for (int i = 0; i < counters.size; ++i)
counters[i] = 0;
return counters.size > 0 && m > 0;
}
bool next(std::vector<int>& counters, int m)
{
for (int i = 0; i < counters.size; ++i)
{
if (++counters[i] < m)
return true;
counters[i] = 0;
}
return false;
}
class Counter {
public:
Counter(int nestedIterations, int iterationLength_) :
iterationLength(iterationLength_
data(nestedIterations + 1)
{
if( nestedIterations == 0 || iterationLength_ == 0 ) {
data.back = 1;
}
}
// An end iterator
Counter :
iterationLength(0
data(1)
{
data[0] = 1;
}
bool operator <(const Counter& rhs) const
{
if( !rhs.isValid ) {
return isValid;
}
// Doesn't really matter for this particular test
//Assert(data.size == rhs.data.size && iterationLength == rhs.iterationLength);
for( int i = data.size - 1; i >= 0; i-- ) {
if( data[i] < rhs.data[i] ) {
return true;
} else if ( rhs.data[i] < data[i] ) {
return false;
}
}
return false;
}
Counter& operator++
{
//Assert(isValid;
for( int i = 0; i < data.size - 1; i++ ) {
data[i]++;
if( data[i] < iterationLength ) {
return *this;
}
data[i] = 0;
}
data.back = 1;
return *this;
}
Counter operator++(int)
{
Counter old(*this);
++(*this);
return old;
}
int operator[](int i) const
{
return data[i];
}
private:
bool isValid const
{
return data.back == 0;
}
int iterationLength;
std::vector<int> data;
};
const int k = 10;
const int m = 10;
void test1
{
int sum = 0;
const Counter end;
for( Counter c = Counter(k, m); c < end; ++c ) {
for( int i = 0; i < k; i++ ) {
sum += c[i];
}
}
cout << sum << endl;
}
такая херь дает
-971566080где-то 30% проигрыша. Но это опять же только итератор.
24.398
-971566080
33.556

Ладно, если что завтра продолжим, приятных сновидений

const Counter end;не похоже на классический итератор из stl
for( Counter c = Counter(k, m); c < end; ++c ) {
к классике ближе будет что-то такое:
CountersSpace space(3, 2);
for (Counters c = space.begin; c != space.end; ++c)
{
...
}
class CountersSpace;
class Counters
{
public:
Counters& operator ++;
bool operator != (const Counters& other);
std::vector<int> state;
private:
Counters(CountersSpace &space, bool isEnd = false);
CountersSpace& space;
friend class CountersSpace;
};
class CountersSpace
{
Counters *_end;
public:
CountersSpace(int k, int m):m(m k(k)
{
_end = new Counters(*this, true);
}
~CountersSpace
{
delete _end;
}
int m;
int k;
const Counters& end
{
return *_end;
}
Counters begin
{
return Counters(*this);
}
};
Counters::Counters(CountersSpace &space, bool isEnd):space(space)
{
state.resize(space.k + 1);
if (isEnd)
{
state[space.k] = 1;
}
else
{
state[space.k] = space.k > 0 && space.m > 0 ? 0 : 1;
}
}
Counters& Counters::operator ++
{
for (int i = 0; i < state.size - 1; ++i)
{
if (++state[i] < space.m)
break;
state[i] = 0;
}
state[space.k] = 1;
return *this;
}
bool Counters::operator != (const Counters& other)
{
if (&this->space != &other.space)
return true;
for(int i = 0; i < state.size; ++i)
{
if (state[i] != other.state[i])
return true;
}
return false;
}

switch нужен затем, что шаблон нельзя раскрывать не константным выражением.
не похоже на классический итератор из stlне обязательно, там есть istream_iterator. Такое да, более распространенный, для контейнеров.
к классике ближе будет что-то такое:




Но ты опять пытаешься соскочить на обсуждение ООПтак насколько я понял, мой код тебе не понравился только тем, что в нем нет ООП. или чем-то другим?
я согласен, что использование std::vector код делает чище.
но вот использование объектной обертки для counters - имхо, излишне:
кода становится больше,
вводятся излишние запреты (например, запрещен прямой доступ к изменению счетчиков
добавляются лишние состояния.
вообще, ты немного напутал. begin/end — это схема получения итераторов у контейнеров, это не входит в контракт самих итераторов, так что мой вариант там норм.
По поводу твоих идиом я еще подумаю, теперь мне уже не кажется все таким плохим, я потом отвечу

begin/end — это схема получения итераторов у контейнеров, это не входит в контракт самих итераторов, так что мой вариант там норм.я исхожу из тезиса, что отсутствие опечаток и полнота решения должны легко перепроверяться за минимальное кол-во перескоков по коду, и за минимальное чтение строчек кода.
Идиома begin/end таким свойством обладает более чем. Достаточно проверить, что begin/end взяты из одного источника, и это дает гарантию, что все элементы будут перечислены, при этом не произойдет зацикливания и т.д.
Предложенный тобой пример такого критерия не имеет, например, по следующим примерам сложно сказать правильные они или не правильные, и что выведут:
//если я забыл какой вариант правильный: начальный должен быть пустой или с числами,
// то что является напоминаем правильного варианта?
for( Counter c = Counter(2, 3); c < Counter; ++c ) {..}
for( Counter c = Counter; c < Counter(2, 3); ++c ){..}
for (Counter c = Counter(2, 3); c < Counter(2, 4); ++c){..} //так можно?
for (Counter c = Counter(2, 3); c < Counter(4, 3); ++c){..} //а так?
Counter c; ++c; //такое правомерно?
ну это я не знаю, просто взял идею с istream_iterator'а, к нему подобные вопросы тоже имеются, хотя совместимость разных итераторов там более-менее понятна (не должно быть ее на первый взгляд). Да, мне стоило реализовать только != и тогда было бы проще, определить ее только по отношению к конечному и к самому себе, и было бы довольно полно.

В этом контракте:
for( bool is_continue = begin(...); is_continue; is_continue = next(...) ) {...}
Меня убило то, что валидность расплывается на две функции. Т.е. можно хотеть избавиться от лишней функции isValid но имхо, так цена слишком велика.
Все-таки тогда уже логично пойти C#-путем и сделать что-то вроде
for( begin(...); next; ) {...}
здесь до вызова next нельзя пользоваться состоянием итератора.
Минус: не подходит под сишный for выглядит чужеродно (не initialize-test-increment).
Т.е. итог такой, что в Си-плюсах родным выглядит именно идиома initialize-test-increment. Как-то так. Обе конструкции, которые есть в этом посте, не так плохи (включая твою, которая мне сперва не понравилась но они чужеродны и при первой встрече вызывают wft (как у меня выше). Но если привыкнуть, то пользоваться можно, наверное, но я пока не вижу плюсов их насаждать в новом коде

Меня убило то, что валидность расплывается на две функции.Если разбираться, то в твоем решении с классом Counter за валидность реально отвечают тоже две функции: конструктор и operator++, а функции IsValid просто является заглушкой для доступа к состоянию этих функций.
По сути, ты просто добавил промежуточный абстрактный слой, сказав, что клиентскому коду не фига знать о таких деталях (что валидность обеспечивается и проверяется в двух функциях, а не в одном месте и это правильное решение с точки зрения классического ООП, но в тоже время - это неправильное решение с точки зрения современных подходов типа agile, где утверждается, что код надо писать только в тот момент, когда он требуется.
Все-таки тогда уже логично пойти C#-путем и сделать что-то вродеу C# другой контракт, примерно, следующий - если убрать объектную оболочку:
code:for( begin(...); next; ) {...}
var state = null;
for (bool is_continue = next(ref state); is_continue; is_continue = next(ref state
{
}
у C# другой контракт
for (bool is_continue = next(ref state); is_continue; is_continue = next(ref state
===
while(next(ref state

agile отменяет метрику WTFs/min?
пределы для простоты одинаковые, только переменные цикла естественно разные, в самом внутреннем цикле все счетчики используются для вычисления некого выражения.
#include <stdlib.h>
#include <stdio.h>
int ipow(a,b){
int r=1;
while(b--) r *= a;
return r;
}
int main(int argc, char **argv){
if (argc < 2) return 1;
int k = atoi(argv[1]);
int l = atoi(argv[2]);
printf("Nested loops: %d\nIndex limit: %d\nIndexes values:\n", k, l);
int i,j;
for (i=0; i < ipow(l,k); i++){
for (j=k-1; j >=0; j--){
printf("%d ", (i%ipow(l,j+1/ipow(l,j;
}
printf("\n");
}
return 0;
}
$ ./nested_cycles_unroll.x 2 3
Nested loops: 2
Index limit: 3
Indexes values:
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1
2 2
#include <stdio.h>
#include <stdlib.h>
int global_a = 0;
typedef struct Lst{
struct Lst *next;
void (*implementint,struct Lst*);
}List;
void my_func(int b, List*p){
int i = 0;
for (; i<b; ++i){
p->implement(b,p->next);
}
}
void norm_func(int b, List*p){
++global_a;
}
List* def{
List * head = NULL;
head = (List*)malloc(sizeof(List;
head->implement = my_func;
head->next = NULL;
return head;
}
void add(List*head){
List*t = head;
while(t->next!=NULL){
t = t->next;
}
List * p = t->next;
p= (List*)malloc(sizeof(List;
p->implement = my_func;
p->next = NULL;
t->next = p;
}
void stop(List * head){
List*t = head;
while(t->next!=NULL){
t = t->next;
}
List*p = t->next;
p = (List*)malloc(sizeof(List;
p->implement = norm_func;
p->next = NULL;
t->next = p;
}
int countToNULL(List*head){
List*p = head;
int count = 0;
while(p!=NULL){
count++;
p=p->next;
}
return count;
}
int main(int argc, char* argv[]){
int i = 0;
List * good_list = NULL;
good_list = def;
for (; i<2; ++i){
add(good_list);
}
stop(good_list);
printf("nodes: %d", countToNULL(good_list;
//compute here
good_list->implement(5,good_list->next);
printf("\n a = %d\n",global_a);
return 0;
}
typedef struct Lst{
struct Lst *next;
void (*implementint,struct Lst*);
}List;
советую выяснить, что такое stl вообще и std::list - в частности

а при чем тут си?
он написал прогу на Си
советую выяснить, что такое stl вообще и std::list - в частностиЕще std::forward_list заодно выясни.
Смешал код и объявления, использовал однострочный комментарий. Оно конечно скомпилируется gcc и будет работать, но не надо говорить что это чисто хардкорный C.
По-моему это классический случай недопонимания разницы между C и C++.
ладно, убедил, это Си. Но все равно тема была про C++.
Тут плюсовое решение только я предлагал, и то засрали тащемта, так что не аргумент.
Смотри.
Его программа — wrong context.
Твое утверждение — wrong proposition in wrong context.
А теперь они два раза перевернутые. (c)
Оставить комментарий
Anna74
В c++ надо написать в зависимости от текущего значения целого параметра k>0Надо k вложенных циклов, пределы для простоты одинаковые, только переменные цикла естественно разные, в самом внутреннем цикле все счетчики используются для вычисления некого выражения.