5

I try to implement that summing up all elements of a vector<vector<int>> in a non-loop ways.
I have checked some relevant questions before, How to sum up elements of a C++ vector?.
So I try to use std::accumulate to implement it but I find it is hard for me to overload a Binary Operator in std::accumulate and implement it.
So I am confused about how to implement it with std::accumulate or is there a better way?
If not mind could anyone help me?
Thanks in advance.

7
  • 4
    I find it is hard for me to overload a Binary Operator in std::accumulate and implement it. Why? What have you tried and why didn't it work? Commented Nov 3, 2019 at 14:29
  • 4
    You can't do it without a loop. std::accumulate has a loop inside it, and by using the algorithm you're just using a loop that was already coded for you. Commented Nov 3, 2019 at 14:33
  • 3
    Don't overload things in the std You also need to show some work. This isn't a tutorial site. You need to ask specific questions about work you've tried. Commented Nov 3, 2019 at 14:34
  • 1
    You don't overload anything, you pass a binary operation which you create down to std::accumulate. It could be a function, a lambda, or a functional object. Commented Nov 3, 2019 at 14:59
  • 1
    The fundamental issue is that you need a loop or you have to add each item sequentially without a loop. The std::accumulate uses a loop. Even specialized processor instructions use a loop. So, why avoid loops? Commented Nov 3, 2019 at 17:52

7 Answers 7

10

You need to use std::accumulate twice, once for the outer vector with a binary operator that knows how to sum the inner vector using an additional call to std::accumulate:

int sum = std::accumulate(
    vec.begin(), vec.end(),                       // iterators for the outer vector
    0,                                            // initial value for summation - 0
    [](int init, const std::vector<int>& intvec){ // binaryOp that sums a single vector<int>
        return std::accumulate(
            intvec.begin(), intvec.end(), // iterators for the inner vector
            init);                        // current sum
                                          // use the default binaryOp here
    }
);
Sign up to request clarification or add additional context in comments.

Comments

7

In this case, I do not suggest using std::accumulate as it would greatly impair readability. Moreover, this function use loops internally, so you would not save anything. Just compare the following loop-based solution with the other answers that use std::accumulate:

int result = 0 ;
for (auto const & subvector : your_vector)
    for (int element : subvector)
        result += element;

Does using a combination of iterators, STL functions, and lambda functions makes your code easier to understand and faster? For me, the answer is clear. Loops are not evil, especially for such simple application.

3 Comments

BTW, the question says non-loop ways. Your for statements are loops.
@ThomasMatthews I wrote this answer to counter the obsession of using the STL library at all cost. I agree that it doesn't answer the question as it is, but I find it to be a better alternative than letting OP go through the path of code clutter. Furthermore, using std::accumulate is also not answering the question as it uses loops inside it. If we follow this logic, then defining a function sum that does the job with loops and then calling this function would also considered a valid answer.
@ThomasMatthews After reading your answer, I see that we both agree on the loop thing, just not on the way of answering ;)
2

According to https://en.cppreference.com/w/cpp/algorithm/accumulate , looks like BinaryOp has the current sum on the left hand, and the next range element on the right. So you should run std::accumulate on the right hand side argument, and then just sum it with left hand side argument and return the result. If you use C++14 or later,

auto binary_op = [&](auto cur_sum, const auto& el){
    auto rhs_sum = std::accumulate(el.begin(), el.end(), 0);
    return cur_sum + rhs_sum;
};

I didn't try to compile the code though :). If i messed up the order of arguments, just replace them.

Edit: wrong terminology - you don't overload BinaryOp, you just pass it.

2 Comments

Why is the lambda set to auto capture by reference when there's nothing to capture?
@Shloim My bad, I've just accustomed to this. Indeed it's not needed.
2

Signature of std::accumulate is:

T accumulate( InputIt first, InputIt last, T init,
              BinaryOperation op );

Note that the return value is deduced from the init parameter (it is not necessarily the value_type of InputIt).

The binary operation is:

Ret binary_op(const Type1 &a, const Type2 &b);

where... (from cppreference)...

The type Type1 must be such that an object of type T can be implicitly converted to Type1. The type Type2 must be such that an object of type InputIt can be dereferenced and then implicitly converted to Type2. The type Ret must be such that an object of type T can be assigned a value of type Ret.

However, when T is the value_type of InputIt, the above is simpler and you have:

using value_type = std::iterator_traits<InputIt>::value_type;
T binary_op(T,value_type&).

Your final result is supposed to be an int, hence T is int. You need two calls two std::accumulate, one for the outer vector (where value_type == std::vector<int>) and one for the inner vectors (where value_type == int):

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

template <typename IT, typename T>
T accumulate2d(IT outer_begin, IT outer_end,const T& init){
    using value_type = typename std::iterator_traits<IT>::value_type;
    return std::accumulate( outer_begin,outer_end,init,
        [](T accu,const value_type& inner){
            return std::accumulate( inner.begin(),inner.end(),accu);
        });
}

int main() {
    std::vector<std::vector<int>> x{ {1,2} , {1,2,3} };
    std::cout << accumulate2d(x.begin(),x.end(),0);
}

Comments

1

Solutions based on nesting std::accumulate may be difficult to understand.

By using a 1D array of intermediate sums, the solution can be more straightforward (but possibly less efficient).

int main()
   {

   // create a unary operator for 'std::transform'
   auto accumulate = []( vector<int> const & v ) -> int
      {
      return std::accumulate(v.begin(),v.end(),int{});
      };

   vector<vector<int>> data = {{1,2,3},{4,5},{6,7,8,9}}; // 2D array

   vector<int> temp; // 1D array of intermediate sums
   transform( data.begin(), data.end(), back_inserter(temp), accumulate );
   int result = accumulate(temp);

   cerr<<"result="<<result<<"\n";

   }

The call to transform accumulates each of the inner arrays to initialize the 1D temp array.

1 Comment

In principle, using implicit loops rather than raw for gives you a standard way of specifying parallelism (if your compiler is up to spec). See: Extensions for parallelism
1

To avoid loops, you'll have to specifically add each element:

std::vector<int> database = {1, 2, 3, 4};
int sum = 0;
int index = 0;
// Start the accumulation
sum = database[index++];
sum = database[index++];
sum = database[index++];
sum = database[index++];

There is no guarantee that std::accumulate will be non-loop (no loops). If you need to avoid loops, then don't use it.

IMHO, there is nothing wrong with using loops: for, while or do-while. Processors that have specialized instructions for summing arrays use loops. Loops are a convenient method for conserving code space. However, there may be times when loops want to be unrolled (for performance reasons). You can have a loop with expanded or unrolled content in it.

Comments

1

With range-v3 (and soon with C++20), you might do

const std::vector<std::vector<int>> v{{1, 2}, {3, 4, 5, 6}};
auto flat = v | ranges::view::join;
std::cout << std::accumulate(begin(flat), end(flat), 0);

Demo

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.