Запустите два алгоритма рядом в одном и том же диапазоне входных итераторов

Если я хочу вычислить сумму группы чисел, полученных из std::istreamЯ могу сделать следующее:

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

Однако, если я хочу рассчитать их среднее значение, мне нужно получить два разных результата:

  • общая сумма (std::accumulate)
  • общее количество (std::distance)

Есть ли способ «объединить» эти два алгоритма и запустить их «бок о бок» за один проход диапазона итераторов? Я хотел бы сделать что-то вроде:

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;

Это возможно?

9

Решение

Готовое решение для такого однопроходного накопления реализуется Boost.Accumulators. Вы делаете один аккумулятор, скажем, для суммы, подсчета и среднего, заполняете его, а затем извлекаете все три результата в конце.

11

Другие решения

Вы не можете объединить два разных алгоритма для чередования. Алгоритмы контролируют поток, и вы можете иметь только один поток. Теперь в вашем конкретном случае вы можете смоделировать это:

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

Это полный взлом, но что-то вроде этого:

#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;
}
-1
По вопросам рекламы [email protected]