Pergunta

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?

Foi útil?

Solução

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.

Outras dicas

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; });

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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top