Run two <algorithm>s side by side on the same input iterator range

311 views Asked by At

If I want to calculate the sum of a bunch of numbers retrieved from an std::istream, I can do the following:

// std::istream & is = ...
int total = std::accumulate(std::istream_iterator<int>(is),
                            std::istream_iterator<int>(),
                            0);

However, if I want to calculate their average, I need to accumulate two different results:

  • the total sum (std::accumulate)
  • the total count (std::distance)

Is there any way to "merge" these two algorithms and run them "side by side" in a single pass of an iterator range? I would like to do something like:

using std::placeholders;
int total, count;
std::tie(total, count) = merge_somehow(std::istream_iterator<int>(is),
                                       std::istream_iterator<int>(),
                                       std::bind(std::accumulate, _1, _2, 0),
                                       std::distance);
double average = (double)total / count;

Is this possible?

3

There are 3 answers

0
Kerrek SB On BEST ANSWER

A ready-made solution for this sort of single-pass accumulation is implemented by Boost.Accumulators. You make a single accumulator, say for sum, count and average, populate it, and then extract all three results at the end.

0
David Rodríguez - dribeas On

You cannot merge two different algorithms to be interleaved. The algorithms control the flow, and you can only have one flow. Now, in your particular case you can simulate it:

int count = 0;
int total = std::accumulate(std::istream_iterator<int>(is),
                            std::istream_iterator<int>(),
                            0,
                            [&](int x, int y) { ++count; return x+y; });
0
AudioBubble On

This is total hack, but something like this:

#include <iostream>
#include <algorithm>
#include <tuple>
#include <iterator>
#include <sstream>

namespace Custom {
    template <class InputIterator, class T, class Bind, typename... Args>
       std::tuple<Args...> accumulate (InputIterator first, InputIterator last, 
           T init, T& output, Bind bind, Args&... args)
    {
      while (first!=last) {
        init = bind(init, *first, args...);
        ++first;
      }
      output = init;
      std::tuple<Args...> tuple(args...);
      return tuple;
    }
}

int main() {
    int total = 0, count = 0;
    std::istringstream is;
    is.str("1 2 3 4 5");
    std::tie(count) = Custom::accumulate(std::istream_iterator<int>(is),
        std::istream_iterator<int>(),
        0,
        total,
        std::bind([&] (int a, int b, int& count) { ++count; return a + b; }, 
        std::placeholders::_1, std::placeholders::_2, std::placeholders::_3),
        count);
    std::cout << total << " " << count;
    return 0;
}