Могу ли я перебрать элементы, которые находятся в одном диапазоне итераторов, но не в другом? - PullRequest
2 голосов
/ 13 мая 2009

Допустим, у меня есть последовательный контейнер и диапазон (пара итераторов) в этом контейнере элементов, которые в данный момент «активны». В какой-то момент я вычисляю новый диапазон элементов, которые должны быть активными, которые могут перекрывать предыдущий диапазон. Затем я хочу перебрать элементы, которые были в старом активном диапазоне, но которые не находятся в новом активном диапазоне, чтобы «деактивировать» их (и аналогично перебрать элементы, которые находятся в новом диапазоне, но не в старом диапазоне, чтобы «активировать» 'их).

Возможно ли это?

Становится ли легче, если я знаю, что начало нового активного диапазона всегда будет в контейнере позже, чем начало старого активного диапазона?

Для целей вопроса предположим, что контейнер является вектором.

Ответы [ 4 ]

2 голосов
/ 13 мая 2009

Что вам нужно, это range_difference функция. Я думал, что Boost.Range предоставит что-то вроде этого, но я ничего не нашел в их документе (ну, я не очень тщательно искал ...), поэтому я свернул свой.

Следующая функция возвращает пару диапазона, содержащую результат разницы между диапазоном, обозначенным (first1, last1), и диапазоном, обозначенным (first2, last2). Предварительным условием является то, что first1 должен быть расположен до или в той же позиции, что и first2.

template <typename InputIterator>
std::pair<
    std::pair<InputIterator, InputIterator>, 
    std::pair<InputIterator, InputIterator> >
range_difference(InputIterator first1, InputIterator last1,
                 InputIterator first2, InputIterator last2)
{
    typedef std::pair<InputIterator, InputIterator> Range;

    InputIterator it;

    // first1 must be <= first2
    for (it = first1 ; it != last1 && it != first2 ; ++it);

    Range left_range = std::make_pair(first1, it); // Left range

    if (it == last1) 
        return std::make_pair(left_range, std::make_pair(first2, first2));

    // it == first2
    while (it != last1 && it != last2) ++it;

    return std::make_pair(left_range, std::make_pair(it, last1)); // Right range
}

Результат разницы может состоять из двух частей, если range2 полностью включен в range1. В итоге вы получаете левый диапазон и правый диапазон:

  |_____________________|__________________|________________________|    
first1                first2             last2                    last1

В этом случае функция возвращает (first1, first2), (last2, last1).

В этой другой конфигурации

  |_____________________|                 |________________________|    
first1                last1             first2                    last2

функция возвращает (first1, last1), (first2, first2). Есть много других возможных конфигураций. Однако важно знать, что в случае, когда правый диапазон пуст, он будет позиционироваться на max (first2, last1) . Вы увидите, как это необходимо в примере.

Наконец, если first1 и first2 находятся в одной и той же позиции, возвращаемый левый диапазон будет пустым, т.е. (First1, first1).

Теперь, как мы можем использовать эту функцию для решения вашей проблемы? Что ж, это довольно просто для диапазона «деактивировать», но немного сложнее для «активировать»:

typedef std::vector<Activable>::iterator Iterator;

Iterator old_beg, old_end, new_beg, new_end; // Old and new ranges

typedef std::pair<Iterator, Iterator> Range;
typedef std::pair<Range, Range> SplitRange;

SplitRange deactivate = range_difference(old_beg, old_end, new_beg, new_end);

// Left range
for (Iterator it = deactivate.first.first;
     it != deactivate.first.second;
     ++it)
    it->deactivate();

// Right range
for (Iterator it = deactivate.second.first;
     it != deactivate.second.second;
     ++it)
    it->deactivate();


SplitRange activate = 
    range_difference(new_beg, new_end, new_beg, deactivate.second.first);
// Note the use of the previously returned right range -------^

for (Iterator it = activate.second.first;
     it != activate.second.second;
     ++it)
    it->activate();

И вот, пожалуйста. Возможно, это решение немного излишне для вашей проблемы, но я думаю, что функция range_difference может быть полезна во многих местах.

2 голосов
/ 13 мая 2009

Вы можете использовать два набора для последнего активного диапазона и другой набор для текущего активного диапазона. Используйте алгоритм set_difference, чтобы получить объекты, которые нужно активировать / деактивировать.

1 голос
/ 13 мая 2009

Я думаю, я сделаю это просто:

// Iterators denoting the old and new ranges (might be cleaner to use some kind
// of typedef like James Hopkin's did, but that's not the most important)
std::vector<Activable>::iterator old_beg,
                                 old_end,
                                 new_beg,
                                 new_end;

std::vector<Activable>::iterator it;

// Deactivate
for (it = old_beg ;                   // go from the beginning of the old range
     it != old_end && it != new_beg ; // to either the end of the old one or the
     ++it)                            // beginning of the new one
    it->deactivate();

// "Jump" to the correct position
if (it == old_end) it = new_beg; // no overlap
else               it = old_end; // overlap

// Activate
for (; it != new_end ; ++it)
    it->activate();

Вы заметите, что я предполагал, что новый диапазон не может полностью содержаться в старом (например, у вас не может быть старого диапазона, идущего от индекса 4 до 10, и нового диапазона от 5 до 7). ). Если это так, вам нужно немного изменить алгоритм.

1 голос
/ 13 мая 2009

Вот простое решение:

typedef std::pair<std::vector<T>::iterator, std::vector<T>::iterator> Range;
void Update(std::vector<T>& v, Range oldActive, Range newActive)
{
    int op = 0;
    for (std::vector<T>::iterator i = v.begin(), end = v.end(); i != end; ++i)
    {
        if (i == oldActive.first) op += 1;
        if (i == oldActive.second) op -= 1;
        if (i == newActive.first) op += 2;
        if (i == newActive.second) op -= 2;
        if (op == 1) i->Deactivate();
        if (op == 2) i->Activate();
    }
}

Это сознательно ставит простоту перед эффективностью в качестве отправной точки, поскольку она сканирует весь вектор; с другой стороны, он однопроходный и не копирует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...