Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using std::accumulate on a two-dimensional std::array

Given the two-dimensional array

std::array<std::array<int, 2>, 3> m = {{ {1, 2}, {3, 4}, {5, 6} }};

I am looking for the sum of all its elements - in this case, 21. Had the array been one-dimensional, I could've written

auto sum = std::accumulate(m.begin(), m.end(), 0);

but for my two-dimensional array, this fails with the rather understandable error

no match for 'operator+' (operand types are 'int' and 'std::array<int, 2ul>')

How can I elegantly compute this sum for my 2D array (avoiding for-loops, preferring STL-algorithms)?

Can it be done with a one-liner like for the one-dimensional case, or does it become more complex?

like image 971
Bart Vandewoestyne Avatar asked Jun 28 '17 15:06

Bart Vandewoestyne


Video Answer


2 Answers

It's just a bit more complex. You have to nest 2 std::accumulate calls. The nested std::accumulate call sums the elements in the nested arrays, and then the first std::accumulate sums those up.

auto sum = std::accumulate(m.cbegin(), m.cend(), 0, [](auto lhs, const auto& rhs) {
    return std::accumulate(rhs.cbegin(), rhs.cend(), lhs);
});

That's a C++14 solution because of the generic lambda, but for C++11, you just need to specify the types explicitly.

like image 163
Rakete1111 Avatar answered Sep 27 '22 18:09

Rakete1111


Conceptually you want to flatten array m and then apply accumulate to it.
Using Range-v3 library (or Ranges TS in the future) you can do just that (link to wandbox).

std::array<std::array<int, 2>, 3> m = {{ {1, 2}, {3, 4}, {5, 6} }};

auto result = ranges::accumulate(ranges::join(m), 0); // flatten range then apply accumulate

That works like what Pete Becker mentioned in comment: "walk through one row of the array and when it hits the end of the row, move to the next row". No copy of subranges made.

like image 28
Serikov Avatar answered Sep 27 '22 17:09

Serikov