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 ¤t){
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();
}