代码之家  ›  专栏  ›  技术社区  ›  Ben Hymers

我可以迭代在一个迭代器范围内但不在另一个迭代器范围内的元素吗?

  •  2
  • Ben Hymers  · 技术社区  · 16 年前

    这可能吗?

    如果我知道容器中新活动范围的开始时间总是晚于旧活动范围的开始时间,这会变得更容易吗?

    4 回复  |  直到 16 年前
        1
  •  2
  •   dirkgently    16 年前

    set_difference 获取要激活/停用的对象的算法。

        2
  •  2
  •   Luc Touraille    16 年前

    你需要的是一个 range_difference

    下面的函数返回一对范围,其中包含由(first1,last1)表示的范围与由(first2,last2)表示的范围之间的差的结果。一个先决条件是,first1必须位于first2之前或与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
    }
    

      |_____________________|__________________|________________________|    
    first1                first2             last2                    last1
    

    在本例中,函数返回(first1,first2),(last2,last1)。

    在另一种配置中,

      |_____________________|                 |________________________|    
    first1                last1             first2                    last2
    

    函数返回(first1,last1),(first2,first2)。还有许多其他可能的配置。然而,需要知道的一件重要事情是,在正确的范围为空的情况下,它将被定位在

    最后,如果first1和first2位于同一位置,则返回的左范围将为空,即ID(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();
    

    距离差 这个函数在很多地方都是有用的。

        3
  •  1
  •   James Hopkin    16 年前

    以下是一个简单的解决方案:

    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();
        }
    }
    

    这故意将简单性置于效率之前作为起点,因为它扫描整个向量;另一方面,它是单通道,不进行复制。

        4
  •  1
  •   Luc Touraille    16 年前

    我想我会保持简单:

    // 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)。如果是这种情况,则需要对算法进行一些更改。