7

Need prettier solution of below example but with std::accumulate.

#include <algorithm>
#include <vector>
#include <iostream>

class Object
{
public:
    Object( double a, double b ):
        a_( a ),
        b_( b )
    {}

    double GetA() const { return a_; }
    double GetB() const { return b_; }
    // other methods
private:
    double a_;
    double b_;
};

class Calculator
{
public:
    Calculator( double& result ):
        result_( result )
    {}

    void operator() ( const Object& object )
    {
        // some formula
        result_ += object.GetA() * object.GetB();
    }
private:
    double& result_;
};

int main()
{
    std::vector< Object > collection;
    collection.push_back( Object( 1, 2 ) );
    collection.push_back( Object( 3, 4 ) );

    double result = 0.0;
    std::for_each( collection.begin(), collection.end(),
                   Calculator( result ) );

    std::cout << "result = " << result << std::endl;

    return 0;
}
Mykola Golubyev
  • 57,943
  • 15
  • 89
  • 102

5 Answers5

13

do changes in Calculator and main function.

struct Calculator
{
    double operator() ( double result, const Object& obj )
    {
        return result + ( obj.GetA() * obj.GetB());
    }

};

int main()
{
    std::vector< Object > collection;
    collection.push_back( Object( 1, 2 ) );
    collection.push_back( Object( 3, 4 ) );

    double result = std::accumulate( collection.begin(), collection.end(), 0, Calculator() );
    std::cout << "result = " << result << std::endl;

    return 0;
}

also it could be better:

double sumABProduct( double result, const Object& obj )
{
    return result + ( obj.GetA() * obj.GetB());
}

double result = std::accumulate( collection.begin(), collection.end(), 0, sumABProduct );
bayda
  • 13,365
  • 8
  • 39
  • 48
3

Update 2: Boost.Lambda makes this a piece of cake:

// headers
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
using namespace boost::lambda;
// ...
cout << accumulate(dv.begin(), dv.end(), 
                   0, 
                   _1 += bind(&strange::value, _2)) //strange defined below
     << endl;

Update: This has been bugging me for a while. I can't just get any of the STL algorithms to work in a decent manner. So, I rolled my own:

// include whatever ...
using namespace std;

// custom accumulator that computes a result of the 
// form: result += object.method();
// all other members same as that of std::accumulate
template <class I, class V, class Fn1, class Fn2>
V accumulate2(I first, I last, V val, Fn1 op, Fn2 memfn) {
    for (; first != last; ++first)
        val = op(val, memfn(*first));
    return val;
}

struct strange {
    strange(int a, int b) : _a(a), _b(b) {}
    int value() { return _a + 10 * _b; }
    int _a, _b;
};

int main() {
    std::vector<strange> dv;
    dv.push_back(strange(1, 3));
    dv.push_back(strange(4, 6));
    dv.push_back(strange(20, -11));        
    cout << accumulate2(dv.begin(), dv.end(), 
                        0, std::plus<int>(), 
                        mem_fun_ref(&strange::value)) << endl;
}

Of course, the original solution still holds: The easiest is to implement an operator+. In this case:

double operator+(double v, Object const& x) {
        return v + x.a_;
}

and make it a friend of Object or member (look up why you may prefer one over the other):

class Object
{
   //...
  friend double operator+(double v, Object const& x);

and you're done with:

 result = accumulate(collection.begin(), collection.end(), 0.0);

My earlier approach doesn't work because we need a binary_function.

std::accumulate manual.

dirkgently
  • 108,024
  • 16
  • 131
  • 187
  • I think the third parameter to accumulate should be 0 not result. result has not yet been defined at that point. – Ferruccio Mar 06 '09 at 11:41
  • It shouldn't. 0 is an int. Pass 0.0, which is a double. – MSalters Mar 06 '09 at 16:39
  • I can't implmenent operator +. Because there will be many others f(object). In one case it just getA()*getB(). In other getC()*getD(). – Mykola Golubyev Mar 07 '09 at 10:23
  • That leaves you with no other choice but to create a set of custom binary functions to pass to templates. Note, you can make them friends of your Object class. – dirkgently Mar 07 '09 at 10:42
1

Using c++0x:

#include <numeric>
#include <vector>
#include <iostream>

class Object
{
  public:
     Object( double a, double b ):
        a_( a ),
        b_( b )
      {}

    double GetA() const { return a_; }
    double GetB() const { return b_; }
    // other methods
  private:
    double a_;
    double b_;
};

int main()
{
    std::vector< Object > collection;
    collection.push_back( Object( 1, 2 ) );
    collection.push_back( Object( 3, 4 ) );
    double result = std::accumulate( collection.begin(), collection.end(), 0,
                                     [] (double result, const Object& obj) 
                                     {
                                       return result + obj.GetA() * obj.GetB();
                                     }
                                   ); 

   std::cout << "result = " << result << std::endl;

   return 0;
} 
1

here is an issue here, I guess the arguments are written in the wrong order should be:

result = std::accumulate(collection.begin(), collection.end(), Object(0),Adapt())
where Adapt is defined thus:

struct Adapt { 
    static double mul(Object const &x) { return x.GetA() * x.GetB(); }
    static Object operator()(Object const &x, Object const &y) { 
       return Object(mul(x)+mul(y)) ; } };

in this case of accumulate, the result is contained in a returned Object.

If you are using gnu parallel mode the functor will give you problems if the result and the actual object referred to by the iterator are different.

struct Adapt { 
        static double mul(Object const &x) { return x.GetA() * x.GetB(); }
        static double operator()(Object const &x, Object const &y) { 
           return mul(x)+mul(y) ; } };
result = std::accumulate(collection.begin(), collection.end(), 0.0,Adapt())

will not work with gnu parallel mode for some strange and silly reason.

0

One would hope this is homework...

struct Adapt { 
    static double mul(Object const &x) { return x.GetA() * x.GetB(); }
    static double operator()(Object const &x, Object const &y) { 
       return mul(x)+mul(y); } };

and

result = std::accumulate(collection.begin(), collection.end(), Object(0,0),Adapt() );

assuming you're not allowed to touch the declaration of Object.

ackb
  • 572
  • 3
  • 6