Output on unique elements of `std :: multiset` and their frequency using std :: algorithm in C ++ (without loops)

I have the following multisetin C ++:

template<class T>
class CompareWords {
public:
    bool operator()(T s1, T s2)
    {
        if (s1.length() == s2.length())
        {
            return ( s1 < s2 );
        }
        else return ( s1.length() < s2.length() );
    }
};
typedef multiset<string, CompareWords<string>> mySet;
typedef std::multiset<string,CompareWords<string>>::iterator mySetItr;
mySet mWords;

I want to print each unique type element std::stringin the set once and next to the element I want to print, how many times it is displayed in the list (frequency), since you can see that the "CompareWord" functor keeps the set sorted.

A solution is proposed here , but this is not what I need, because I'm looking for a solution without using (while, for, do while).

I know I can use this:

//gives a pointer to the first and last range or repeated element "word"
auto p = mWords.equal_range(word);
// compute the distance between the iterators that bound the range AKA frequency 
int count = static_cast<int>(std::distance(p.first, p.second));

but i cant come up with a solution without loops?

+4
source share
3
#include <iostream>
#include <algorithm>
#include <set>
#include <iterator>
#include <string>

int main()
{
    typedef std::multiset<std::string> mySet;
    typedef std::multiset<std::string>::iterator mySetItr;

    mySet mWords;

    mWords.insert("A");
    mWords.insert("A");
    mWords.insert("B");

    mySetItr it = std::begin(mWords), itend = std::end(mWords);
    std::for_each<mySetItr&>(it, itend, [&mWords, &it] (const std::string& word)
    {
        auto p = mWords.equal_range(word);
        int count = static_cast<int>(std::distance(p.first, p.second));
        std::cout << word << " " << count << std::endl;
        std::advance(it, count - 1);
    });
}

:

A 2
B 1

.

+1

, . , , std::multimap, ( ).

, , , .

template<class Iterator, class Clumps, class Compare>
void produce_clumps( Iterator begin, Iterator end, Clumps&& clumps, Compare&& compare) {
  if (begin==end) return; // do nothing for nothing
  typedef decltype(*begin) value_type_ref;
  // We know runs are at least 1 long, so don't bother comparing the first time.
  // Generally, advancing will have a cost similar to comparing.  If comparing is much
  // more expensive than advancing, then this is sub optimal:
  std::size_t count = 1;
  Iterator run_end = std::find_if(
    std::next(begin), end,
    [&]( value_type_ref v ){
      if (!compare(*begin, v)) {
        ++count;
        return false;
      }
      return true;
    }
  );
  // call our clumps callback:
  clumps( begin, run_end, count );
  // tail end recurse:
  return produce_clumps( std::move(run_end), std::move(end), std::forward<Clumps>(clumps), std::forward<Compare>(compare) );
}

. :

int main() {
  typedef std::multiset<std::string> mySet;
  typedef std::multiset<std::string>::iterator mySetItr;

  mySet mWords { "A", "A", "B" };

  produce_clumps( mWords.begin(), mWords.end(),
    []( mySetItr run_start, mySetItr /* run_end -- unused */, std::size_t count )
    {
      std::cout << "Word [" << *run_start << "] occurs " << count << " times\n";
    },
    CompareWords<std::string>{}
  );
}

( ), .

( ). ( ) ( 1). N , , N + C + 1 (N = , C = ).

+2

The following does the job without an explicit loop using recursion:

void print_rec(const mySet& set, mySetItr it)
{
    if (it == set.end()) {
        return;
    }
    const auto& word = *it;
    auto next = std::find_if(it, set.end(),
        [&word](const std::string& s) {
            return s != word;
        });
    std::cout << word << " appears " << std::distance(it, next) << std::endl;
    print_rec(set, next);
}

void print(const mySet& set)
{
    print_rec(set, set.begin());
}

Demo

+1
source

All Articles