std::count_if and std::find_if

Another place where it comes handy to use boost::bind or the lambda expression is as predicate of the conditional version for the STL algorithms count_if and find_if.

We use count_if when we want to get the number of elements in a sequence that respect a clause we specify; find_if is used to find the fist element in a sequence matching our requirements.

Without using boost or C++11 we should define a functor to specify the behavior that we are interested in. As we can see in this example, the new techniques make the code cleaner and more readable:

#include <iostream>
#include <vector>
#include <functional>
#include <algorithm>
#include <iterator>

#include "boost/bind.hpp"

using namespace std;

namespace
{
   template<class T>
   void dump(vector<T>& vec)
   {
      copy(vec.begin(), vec.end(), ostream_iterator<T>(cout, " "));
      cout << endl;
   }
}

void bind04()
{
   vector<int> vec;

   vec.push_back(12);
   vec.push_back(7);
   vec.push_back(4);
   vec.push_back(10);
   dump(vec);

   cout << endl << "Using boost::bind" << endl;

   cout << "Counting elements in (5, 10]: ";

   // 1
   auto fb = boost::bind(logical_and<bool>(),
   boost::bind(greater<int>(), _1, 5),
   boost::bind(less_equal<int>(), _1, 10));
   int count = count_if(vec.begin(), vec.end(), fb);
   cout << "found " << count << " items" << endl;

   cout << "Getting first element in (5, 10]: ";
   vector<int>::iterator it = find_if(vec.begin(), vec.end(), fb);
   if(it != vec.end())
      cout << *it << endl;

   cout << endl << "Same, but using lambda expressions" << endl;

   cout << "Counting elements in (5, 10]: ";

   // 2
   auto fl = [](int x){ return x > 5 && x <= 10; };
   count = count_if(vec.begin(), vec.end(), fl);
   cout << "found " << count << " items" << endl;

   cout << "Getting first element in (5, 10]: ";
   it = find_if(vec.begin(), vec.end(), fl);
   if (it != vec.end())
      cout << *it << endl;
}
1. since we use the same predicate a couple of times it's a good idea storing it in a local variable (here using the cool C++11 'auto' keyword to save the time from figuring out the correct type definition for the predicate). The construct is a bit verbose, but should be clear enough in its sense: we are looking for a value in the interval (5, 10]; count_if will count all the elements in the sequence respecting this rule; find_if will return the iterator to the first element for which that is valid - or end() if no one will do.
2. it is so much cleaner implementing the same functionality using the C++11 lambda syntax.

The code is based on an example provided by "Beyond the C++ Standard Library: An Introduction to Boost", by Björn Karlsson, an Addison Wesley Professional book. An interesting reading indeed.

No comments:

Post a Comment