C++ 在没有原始循环的情况下累积相等的范围

C++ 在没有原始循环的情况下累积相等的范围,c++,c++11,c++14,C++,C++11,C++14,在我正在从事的一个项目中,我到处都有这样的问题:我有数据,这些数据必须按照某种排序标准进行分组,而且这些组必须进行累积 听起来很简单: 对输入范围进行排序(std::Sort) 识别等效条目(std::equal_range) 累加每个相等的范围(std::累加) 将累加结果存储在某个输出范围内 以下是我的想法: // accumulate equal ranges #include <cassert> #include <algorithm> #include <

在我正在从事的一个项目中,我到处都有这样的问题:我有数据,这些数据必须按照某种排序标准进行分组,而且这些组必须进行累积

听起来很简单:

  • 对输入范围进行排序(
    std::Sort
  • 识别等效条目(
    std::equal_range
  • 累加每个相等的范围(
    std::累加
  • 将累加结果存储在某个输出范围内
  • 以下是我的想法:

    // 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);
      }
    }
    
    //累积相等的范围
    #包括
    #包括
    #包括
    模板
    自动累积相等范围(先转发,
    最后呢,,
    比较,,
    首先输出dest_,
    T首字母,
    总和)->无效
    {
    断言(按顺序排列(第一、最后、比较));
    auto equalRange=std::make_pair(第一个,最后一个);
    for(;first!=last;first=equalRange.second)
    {
    equalRange=std::equal_范围(第一,最后,*第一,比较);
    *第一目的地=
    std::累加(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}}));
    }
    
    //测试代码
    #包括
    #包括
    int main()
    {
    使用IP=std::pair;
    自动值=标准::向量{{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}};
    自动结果=标准::向量{};
    累积相等范围(开始(值),
    结束(值),
    [](常数IP&l,常数IP&r)//小于
    {
    返回l.first

    < >使用现代C++特性有一个很好的方法来去掉上面代码中的for循环吗?< /p> ,我不知道用相同的方法来删除原始循环的方法。主要的问题是,没有任何标准的算法可以让您跳转到输入范围中的不同位置,而不是连续地遍历每个位置。如果有一种方法可以为何时停止指定单独的谓词,而不是使用固定的结束位置,那么执行
    std::generate
    几乎就是您想要的

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

    模板
    自动累计\u相等\u范围\u新(先转发,
    最后呢,,
    比较,,
    首先输出dest_,
    T首字母,
    总和)->无效
    {
    如果(最后一次==第一次)返回;
    自动iter=第一;
    auto prev=*iter++;
    自动范围_sum=总和(初始值、上一个值);
    std::每个(
    国际热核聚变实验堆,
    最后,,
    [&](常数自动和当前){
    如果(比较(上一个,当前)){
    *dest_first++=范围_和;
    范围总和=初始值;
    }
    范围总和=总和(范围总和,电流);
    prev=当前值;
    }
    );
    *dest_first++=范围_和;
    }
    

    这样做的好处是它具有更好的最坏情况复杂性(O(n)而不是O(n*log(n)),但只需要对算法进行一点小的更改即可解决这一问题(使用
    std::nextant_find
    而不是
    std::equal_range
    )。另一个优点是它可以与输入迭代器而不是前向迭代器一起工作。

    'No裸for'仅适用于主上下文

    即:

    //坏的
    void foo(){
    设置();
    
    对于(i=0;i您应该将您的迷你测试用例放在这里。我们喜欢网站上的东西,它们在未来不会消亡。:)这就是为什么您首先编写这个函数的原因。所以不清楚为什么去掉它很重要。您正在编写一个算法函数;允许使用for循环。
    assert
    不是用于错误处理的。@erip添加了测试代码。感谢您的提示。我正在谈论您实现的效率。您使用的是
    std::equal\range
    是O(logn)。这是次优。线性搜索(
    find_,如果
    可能的话)在这里看起来更有效。(不,我认为你不能摆脱外部循环。无论如何都不容易。)很好!线性遍历数据可能更好。顺便说一句,你可以去掉
    iter
    ,用
    initial
    初始化
    range\u sum
    ,并让每个
    第一次运行到
    最后一次运行。
    
    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;
    }
    
    // bad
    void foo() {
        setup();
        for (i=0; i<10; i++) {
             do_something(i);
        }
        teardown();
    }
    
    // better
    void bar() {
        setup();
        do_somethings();
        teardown();
    }