简体   繁体   中英

Is there something like std::accumulate that operates on iterators?

What I want: An iterator to the next element after another iterator (let's call it base iterator) that's lexicographically larger (>) than the element pointed to by that base iterator (that is, if I have an 'a' and the rest is [c,d,a,b] I want the b, or rather the iterator to b). I'm not aware of an <algorithm> function that could be used for that, so I'm doing it "by hand" if you so will.

How I think I could do it: run this lambda with some kinda of accumulate over the collection:

[mism.first](it_t acc, it_t el) 
{ 
    if(*el > *mism.first) { 
        return iter_min(acc,el); 
    } 
    return acc; 
}

(iter_min(a,b) is a when *a<=*b, mism.first is said "base iterator"). But since accumulate works on values rather than iterators, I can't. Is there something like this for iterators? If not, would you say I should just write it (I don't think I'll be overexercising myself with that) or am I just going at it the "wrong" way (by the way, I'd be ending up swapping the elements pointed to by the iterators. I know this sounds an awful lot like next_permutation, but while what I'm doing has a lot to do with permutations, it's not quite what I want)?

The easiest possibility I see is to create a range that can be iterated consisting of with successive iterators from [base, end) . This is possible with the Boost.Iterators counting_iterator , but should be fairly easy to implement even without boost.

Your proposed lambda would then work almost as is:

some_iterator base = ..., end = ...;
some_iterator the_next_least_thing =
  std::accumulate(boost::make_counting_iterator(base),
                  boost::make_counting_iterator(end),
                  end,
                  [=](some_iterator acc, some_iterator cur) { 
                     return (*cur > *base && (acc == end || *cur < *acc)) ? cur : acc });

To find the first element that is larger than the base, you could try to use std::find_if with a lambda predicate that returns true if the current element pointed to is larger than the element pointed to by the base iterator. Since lambdas are unfortunately not polymorphic, you need to specify the type using std::iterator_traits (so that it also works for pointers).

To find the smallest element larger than the base, you do a linear scan where you repeatedly look for the first element in the remaining list that is larger than the base, and then check if it is smaller than the current minimum. Every time you update the minimum, you also increase the iterator to the current minimum, so that you only have to look for the next candidate in the remaining section. This makes this algorithm O(N) in the number of elements N .

#include <algorithm>
#include <array>
#include <iostream>
#include <iterator>

template<typename FwdIt>
FwdIt first_larger(FwdIt first, FwdIt last, FwdIt base)
{
    typedef typename std::iterator_traits<FwdIt>::value_type T;

    return std::find_if(first, last, [=](T const& elem) {
         return elem > *base;
    });
}

template<typename FwdIt>
FwdIt first_larger(FwdIt first, FwdIt last)
{
    return first_larger(first, last, first);
}

template<typename FwdIt>
FwdIt min_larger(FwdIt first, FwdIt last)
{
    typedef typename std::iterator_traits<FwdIt>::value_type T;
    auto min = last;
    auto found = false;

    for(auto it = first; it != last; ++it) {
         auto m = first_larger(it, last, first);         
         if (m == last) break;         
         it = min = m;
         found = true;        
    }
    return found? min : last;
}

int main()
{
    std::array<int,11> arr = {{ 54, 314, 5, 7, 1, -1, 0, 14, 9, 8, 6 }};

    auto b = &arr[3]; 
    auto f = first_larger(b, arr.end());
    auto m = min_larger(b, arr.end());

    std::cout << *b << "\n"; // 7
    if(f != arr.end()) std::cout << *f << "\n"; // 14
    if(m != arr.end()) std::cout << *m << "\n"; // 8 

    return 0;
}

You could probably generalize this to a function

template<typename FwdIt, typename Pred, typename Cmp>
FwdIt min_element_if(FwdIt first, FwdIt last, Pred pred, Cmp cmp)
{
    // return iterator to smallest element satisfying Predicate
}

and other overloads that set Cmp equal to operator< etc. Unfortunately, the STL does not contain such combinations of more elementary algorithms. You could look at Boost.Iterator to use filter adapters

 // equivalent to min_element_if(first, last, pred)
 min_element(boost::make_filter_iterator(first, last, pred),
             boost::make_filter_iterator(last, last, pred));

Use find_if, supplying your iterator+1 as the start point:

bool mycomp (char c1, char c2)
{ return lexicographical_compare(*c1,*c1,*c2,*c2); }    

vector<char>::iterator nextBigger = find_if(it+1,end,mycomp)

Adapted from std lib's lexicographic_compare routine and find_if .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM