Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Find vector elements with even values

Could you explain how does this code work. It successfully counts vector elements with even values, but it's not clear for me how does binding in this particular case work.

count_if(vec.begin(), vec.end(),
         std::bind(logical_not<bool>(),
                   std::bind(modulus<int>(), placeholders::_1, 2)));
like image 374
Leopoldo Avatar asked Apr 18 '14 13:04

Leopoldo


3 Answers

Note that the code you posted counts the even numbers in the vector, not the odd ones:

count_if(vec.begin(), vec.end(),
         bind(logical_not<bool>(),
              bind(modulus<int>(), placeholders::_1, 2)));

The count_if is an algorithm that returns the number of elements in the specified range satisfying specific criteria:

count_if(first, last, criteria)

In your case, first is vec.begin() and last is vec.end(): so the whole vector is considered for the count.

Now let's focus the attention on the criteria part.

Starting from inner and going outer:

modulus<int> is a function object that returns the remainder of an integer division (just like % operator). It takes two arguments: the first is expressed as placeholders::_1, which is the generic element in the source vector. Think of it as a variable x that scans the entire vector content.

The second argument is the number 2, since to check if an integer is even or odd, you can calculate x % 2 and compare the result with 0:

x % 2 == 0 --> even number
x % 2 == 1 --> odd number

bind is used to specify the arguments of the modulus function object.

The result of this modulus operation is given as input to another function object: logical_not<bool>. This just negates the input, e.g. if the input was false (0), logical_not<bool> returns true, and viceversa.

So, the "counting criteria" is expressed by this flow of operations:

  1. Calculate: placeholders::_1 % 2, i.e. <<generic vector element>> % 2, using modulus.
  2. If the result of the above operation is 0 (false), return true (and viceversa), using logical_not.

So, if a number is even:

  1. even number % 2 == 0
  2. negating 0 you get true.

Instead, if a number is odd:

  1. odd number % 2 == 1
  2. negating 1 you get false.

Since count_if counts the number of elements for which the criteria is true, you are counting the even numbers in the vector.

If you really want to count the odd numbers in the vector, you can just get rid of the logical inversion (i.e. logical_not):

auto odds = count_if(vec.begin(), vec.end(),
                     bind(modulus<int>(), placeholders::_1, 2));    

Note that in this case the functional approach using modulus and logical_not seems too complicated: using a lambda (or even an ad hoc IsEven() simple function) would be clearer.
Consider the following code (live here on Ideone) for a comparison between the three approaches:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>
using namespace std;

bool IsEven(int n) {
    return (n % 2) == 0;
}

int main() {
    // Test vector
    vector<int> vec{ 11, 22, 33, 44, 55 };

    // Using functional approach
    auto n = count_if(vec.begin(), vec.end(),
         bind(logical_not<bool>(),
             bind(modulus<int>(), placeholders::_1, 2)));    
    cout << n << endl;

    // Using lambdas
    n = count_if(vec.begin(), vec.end(), 
                 [](int n) { return (n % 2) == 0; });
    cout << n << endl;

    // Using boolean returning ad hoc function
    n = count_if(vec.begin(), vec.end(), IsEven);
    cout << n << endl;
}
like image 200
Mr.C64 Avatar answered Oct 22 '22 14:10

Mr.C64


Unravel from inner call to outer call:

modulus<int>( a, 2 )

returns the remainder of something after a division by 2: ==0 or !=0.

logical_not<bool>( x )

does a logical inversion of x (so 0/false becomes 1/true and 1/true becomes 0/false)

count_if(from, to, cond )

evaluates cond (the inverted modulus) for all elements defined by the iterator from/to.

And the placeholders::_1 is the hardwired way for inserting something determined in the iterator-driven loop run by count_if (i.e., the current element) into the function nested down below.

like image 22
laune Avatar answered Oct 22 '22 14:10

laune


This doesn't count odd elements but even elements.

To count vector elements with odd values we have to check each element for division by 2 and return true if the result is 1.

So we will use the modulus(), which is a function object that implements operator()

constexpr T operator()( const T& lhs, const T& rhs ) const;

and returns the remainder of the division of lhs by rhs.

We have to use std::bind to glue one and only one argument passed to

count_if( InputIt first, InputIt last, UnaryPredicate p )

unary predicate p (which is our modulus here) to first argument of modulus and constant 2 to second

std::bind(modulus<int>(), placeholders::_1, 2))

Now our function std::bind(modulus<int>(), placeholders::_1, 2)) returns true (1) if argument is odd and false (0) if argument is even. If we want to count even arguments we must neglect this, so our predicate must return the opposite:

std::bind(logical_not<bool>(),
                           std::bind(modulus<int>(), placeholders::_1, 2))

http://ideone.com/80VmsZ

As Mike Seymour suggested, simpler and cleaner design would be to replace this binds with short lambda function:

[](int x){return x % 2 == 0;} // to count even elements
[](int x){return x % 2 != 0;} // to count odds
like image 20
4pie0 Avatar answered Oct 22 '22 14:10

4pie0