用accumulate或for_each来统计区间
一般的统计算法可以使用count和count_if来替代,他们告诉你某个区间有多少等于某个值的元素。
而accumulate和for_each是对于区间的的统计。
accumulate
看个简单例子:
list<double> ld = { 0.0, 0.1, 1.1, 2.2, 3.0 };
double sum = std::accumulate(ld.begin(), ld.end(), 1.1);
cout << "sum :" << sum << endl;
这个结果就是sum:7.5,他是从第一个元素1.1开始逐步累加区间的数值
我们还可以按照自己的想法给定一个仿函数实现各种累加:
string::size_type stringLenSum(string::size_type sum, const string& s)
{
return sum + s.size();
}
set<string> ss = { "a", "abc" };
string::size_type ssum = accumulate(ss.begin(),
ss.end(), 0, stringLenSum);
cout << "ssum:" << ssum << endl;
打印结果为4。
让他计算区间的积将更简单,因为我们可以使用标准的multiplies仿函数类:
vector<float> vf = { 1.0f, 1.2f, 2.0f };
float product = accumulate(vf.begin(), vf.end(),
1.0f, multiplies<float>());
cout << "vf : " << product << endl;
计算结果为:2.4f。特别注意计算乘积起始值如果为0,那么结果都是0了。
最后一个晦涩点的例子,他用来寻找point区间的平均值,并且我们也可以看一下accumulate和for_each的区别。
struct Point
{
Point(double x_, double y_) :x(x_), y(y_) {};
double x;
double y;
};
//增加一个处理的仿函数,我们可以按照以下方式使用
vector<Point> lp =
{
{ Point(1.0, 2.0) },
{ Point(2.0, 4.0) },
{ Point(3.0, 6.0)},
};
Point avg = std::accumulate(lp.begin(), lp.end(), Point(0,0), PointAvg());
cout << "Point:" << avg.x << " " << avg.y << endl;
接下来是使用accumulate方式的仿函数实现:
class PointAvg
{
public:
PointAvg():nums(0),xSum(0.0),ySum(0.0){}
const Point operator()(const Point& avg, const Point& p)
{
++nums;
xSum += p.x;
ySum += p.y;
return Point(xSum / nums, ySum / nums);
}
private:
size_t nums;
double xSum;
double ySum;
};
然而实际上,成员变量的nums,xSum和ySum的存在,可能会造成副作用,技术上讲,以上代码会导致结果未定义。所以for_each出场了。
- 除了副作用问题,for_each和accumulate的不同主要在两个方面。首先,accumulate的名字表示它是一个产生区
间统计的算法,for_each听起来好像你只是要对区间的每个元素进行一些操作,而且,当然,那是那个算法
的主要应用。用for_each来统计一个区间是合法的,但是它没有accumulate清楚。 - 其次,accumulate直接返回那些我们想要的统计值,而for_each返回一个函数对象,我们必须从这个对象中提取想要的统计信息。在C++里,那意味着我们必须给仿函数类添加一个
成员函数
,让我们找回我们追求的统计信息。
class PointAvg
{
public:
PointAvg():nums(0),xSum(0.0),ySum(0.0){}
void operator()(const Point& p)
{
++nums;
xSum += p.x;
ySum += p.y;
}
Point result() const
{
return Point(xSum / nums, ySum / nums);
}
private:
size_t nums;
double xSum;
double ySum;
};
Point avg2 = std::for_each(lp.begin(), lp.end(), PointAvg()).result();
cout << "Point:" << avg2.x << " " << avg2.y << endl;
就个人来说,我更喜欢用accumulate来统计,因为我认为它最清楚地表达了正在做什么,但是for_each也可
以,而且不像accumulate,副作用的问题并不跟随for_each。两个算法都能用来统计区间。使用最适合你的那
个。
你可能想知道为什么for_each的函数参数允许有副作用,而accumulate不允许。这是一个刺向STL心脏的探针
问题。唉,尊敬的读者,有一些秘密总是在我们的知识范围之外。为什么accumulate和for_each之间有差别?
我尚待听到一个令人信服的解释。 — 原作者。