2013-12-10 40 views
9

如果我想計算一羣來自std::istream檢索數的總和,我可以做以下並排:運行兩個<algorithm> s的同樣輸入迭代範圍

// 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; 

這可能嗎?

回答

11

這種單通積累的現成解決方案由Boost.Accumulators執行。你創建一個單一的累加器,比如總和,計數和平均值,填充它,然後在最後提取所有三個結果。

9

您不能合併兩個不同的算法進行交錯。算法控制流量,並且只能有一個流量。現在,在您的特定情況下,你可以模擬它:

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

這是總的黑客,但這樣的事情:

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