Лучший синтаксис для выполнения действия между каждой итерации цикла.


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

В смысле, что это лучший способ в C/C++ для реализации этого вымышленного конструкта:

for (...) {
} between {
}

С помощью такой конструкции можно легко реализовать строку соединения (с): (псевдокод)

result = "";
foreach ( item : input ) {
    result += str(item);
} between {
    result += sep;
}

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

  • перемещение "между" код в "если (!первая/последняя итерация)" внутри цикла.
    • Это является метод, когда указатель/итератор/свободно результату магазинах понятие первой/последней итерации (например, для проверки значений 0, .пустая(), нуль и т. д.).
  • превратить "тело цикла" в функцию и вызывать ее из двух местах: перед и во время цикла, и изменить цикл, чтобы пропустить первый элемент. (незначительные дублирования кода)
    • Это-это метод, когда в "теле цикла" - это один вызов функции

Ни один из этих совершенно обобщенное решение, и, хотя его всего несколько строк + переменной состояния, я пытаюсь найти идеальное решение.

Приоритеты:

  • Нет ненужных источник дублирования кода (но готов принять двоичный дублирования кода)
  • Эффективность
  • Ясная семантика
  • Тривиально использования (простой синтаксис ака, несколько требований библиотека)


378
3
c++ c
задан 4 мая 2011 в 04:05 Источник Поделиться
Комментарии
2 ответа

Ближайший я пришел к этому в C или C++ - это небольшая модификация для кнута -и-а-половина:

template<class T>
void print(std::vector<T> const &x) {
std::cout << '[';
typename std::vector<T>::const_iterator
begin = x.begin(),
end = x.end();
if (begin != end) { // Duplicated condition to handle the empty case.
while (true) { // Here is the "loop and a half".
std::cout << *begin;
if (++begin == end) break;
std::cout << ", ";
}
}
std::cout << "]\n";
}

В C++0х позволяет обобщить:

template<class Iter, class Body, class Between>
void foreach(Iter begin, Iter end, Body body, Between between) {
if (begin != end) { // This duplication doesn't matter as it is
// wrapped up in a library function.
while (true) {
body(*begin);
if (++begin == end) break;
between();
}
}
}

template<class T>
void print(std::vector<T> const &x) {
std::cout << '[';
foreach(x.begin(), x.end(),
[](T const& v) {
std::cout << v;
// Long code here is still readable.
},
[]{
std::cout << ", ";
// Long code here is still readable.
});
std::cout << "]\n";
}

Захват лямда даже позволяет изменять локальные переменные в функции вызова оператора foreach.

5
ответ дан 4 мая 2011 в 05:05 Источник Поделиться

Прочитайте образец о посетителе...

Для более сложного объекта, образец:

template<class T>
struct Walker
{
Walker(Item *parent): m_parent(parent){}
Walker(Item *parent, T const& f): m_parent(parent), f(f){}
// visit all elements
Walker& Walk()
{
TestClass::iterator it = parent->begin(), end = parent->end();
for( ; it != end; ++it)
{
f.Visit(*it);
}
return *this;
}
// get visited result
T const& GetF()const { return f; }
private:
Walker& operator = (Walker const& rhs);
Item* m_parent;
T f;
};

и определение класса, например:

struct VisitorClass
{
void Visit(TestClass*){}
};

после этого использовать его:

Walker<VisitorClass> testVisit(this);

-2
ответ дан 4 мая 2011 в 08:05 Источник Поделиться