积累相等的范围,没有原始循环

时间:2016-01-10 15:02:35

标签: c++ c++11 c++14

在我正在开展的其中一个项目中,我遇到了这个问题:我的数据必须按某种排序标准进行分组,并且必须累积这些组。

听起来很简单:

  1. 对输入范围(std::sort
  2. 进行排序
  3. 确定等效条目(std::equal_range
  4. 累积每个相等的范围(std::accumulate
  5. 将累积结果存储在某个输出范围
  6. 以下是我提出的建议:

    // accumulate equal ranges
    #include <cassert>
    #include <algorithm>
    #include <numeric>
    
    template <typename ForwardIt,
              typename Compare,
              typename OutputIt,
              typename T,
              typename Sum>
    auto accumulate_equal_ranges(ForwardIt first,
                                 ForwardIt last,
                                 Compare compare,
                                 OutputIt dest_first,
                                 T initial,
                                 Sum sum) -> void
    {
      assert(is_sorted(first, last, compare));
    
      auto equalRange = std::make_pair(first, last);
    
      for (; first != last; first = equalRange.second)
      {
        equalRange = std::equal_range(first, last, *first, compare);
        *dest_first =
            std::accumulate(equalRange.first, equalRange.second, initial, sum);
      }
    }
    

    这是一个迷你测试案例:

    // Test code
    
    #include <iostream>
    #include <vector>
    int main()
    {
      using IP = std::pair<int, int>;
      auto values = std::vector<IP>{{0, 1},
                                    {0, 2},
                                    {0, 3},
                                    {0, 4},
                                    {1, 0},
                                    {1, 0},
                                    {1, 0},
                                    {1, 0},
                                    {2, 4},
                                    {2, 3},
                                    {2, 2},
                                    {2, 1},
                                    {3, 7},
                                    {3, 7},
                                    {3, 7},
                                    {3, 7},
                                    {4, 23},
                                    {5, 42}};
    
      auto result = std::vector<IP>{};
      accumulate_equal_ranges(begin(values),
                              end(values),
                              [](const IP& l, const IP& r)  // less
                              {
                                return l.first < r.first;
                              },
                              back_inserter(result),  // back_inserter
                              IP{0, 0},
                              [](const IP& l, const IP& r)  // plus<pair>
                              {
                                return std::make_pair(r.first, l.second + r.second);
                              });
    
      assert((result == std::vector<IP>{
                            {0, 10}, {1, 0}, {2, 10}, {3, 28}, {4, 23}, {5, 42}}));
    }
    

    有没有一种很好的方法可以使用现代C ++功能摆脱上面代码中的for循环?

2 个答案:

答案 0 :(得分:2)

我没有看到使用相同的算法方法删除原始循环的方法。主要问题是没有任何标准算法可以让您跳转到输入范围内的不同位置,而不是连续单步执行每个位置。如果有办法为停止时间指定单独的谓词而不是使用固定的结束位置,那么执行std::generate几乎就是你想要的。

但是,如果采用不同的方法,可以使用std::for_each()

template <typename ForwardIt,
          typename Compare,
          typename OutputIt,
          typename T,
          typename Sum>
auto accumulate_equal_ranges_new(ForwardIt first,
                             ForwardIt last,
                             Compare compare,
                             OutputIt dest_first,
                             T initial,
                             Sum sum) -> void
{
  if (last==first) return;

  auto iter = first;
  auto prev = *iter++;
  auto range_sum = sum(initial,prev);

  std::for_each(
    iter,
    last,
    [&](const auto &current){
      if (compare(prev,current)) {
        *dest_first++ = range_sum;
        range_sum = initial;
      }
      range_sum = sum(range_sum,current);
      prev = current;
    }
  );

  *dest_first++ = range_sum;
}

这样做的好处是它具有更好的最坏情况复杂度(O(n)而不是O(n * log(n))),但它只需要对您的算法进行少量更改即可解决(使用std::adjacent_find代替std::equal_range)。另一个优点是它可以使用输入迭代器而不是前向迭代器。

答案 1 :(得分:0)

'没有裸体'只适合你的主要背景。

即:

// bad
void foo() {
    setup();
    for (i=0; i<10; i++) {
         do_something(i);
    }
    teardown();
}

// better
void bar() {
    setup();
    do_somethings();
    teardown();
}

do_somethings有一个循环 - 裸体fors没有被禁止 - 你只是不应该在你的主要背景下看起来。考虑算法而不是循环。