STL算法——accumulate
来源:互联网 发布:jquery.md5.js cdn 编辑:程序博客网 时间:2024/06/07 03:22
如果有一个算法可以让你做各种事, 那这个算法一定是 std:accumulate.
知道怎样使用它,以及怎样不使用它非常重要。
基本用法
Numeric 类型
第一件需要知道的事情就是std::accumulate 的所在位置: 它在<numeric>头文件中,而不是像其他兄弟算法一样在<algorithm>头文件中。
正如 Scott Meyers 的《Effective STL》条目37, std::accumulate 用于统计一个区间(范围),换而言之,std::accumulate 把一组元素提炼成一个对象,一个数。
如果你没有明确指明, std::accumulate 不会对区间内所有元素进行sum。 sum 是通过符号+来进行计算的。 如果我们需要对两个值进行+运算, 那么我们同时需要一个初始值。
以下是使用原型:
template<typename InputIterator, typename T>T accumulate(InputIterator first, InputIterator last, T initialValue);因此, 对于一组数字集合, std::accmulate 把它们汇总起来:
std::vector<int> numbers = { 2, 9, -4, 2 };int sum = std::accumulate(begin(numbers), end(numbers), 0);注意此处存在一个陷进。 以上代码片段中,初始值是整型,但是请看下面的代码片段中,不包含整型数字的集合:
std::vector<double> doubles = { 1.5, 2, 3.5 };double sum = std::accumulate(begin(doubles), end(doubles), 0);你能预测到它的输出吗?
它的结果 sum = 6。
是不觉得很惊奇,因为1.5+2+3.5=7, 不是6。
为了理解发生类什么,请回头看下 std::accmulate 的原型:
template<typename InputIterator, typename T>T accumulate(InputIterator first, InputIterator last, T initialValue);
注意 类型 T 和集合区间中的元素类型不一定相关。 在我们的调用中, 它是由第三个参数 0 推导出的,因为0是int,所以T是 int。 所以std:;accumulate 以int类型工作, 对每次累加运算进行类去尾截整。
可以简单修改下, 把第三个参数0 改成一个double 类型数值。
std::vector<double> doubles = { 1.5, 2, 3.5 };double sum = std::accumulate(begin(doubles), end(doubles), 0.);这样最后的sum结果就是7。
这个例子值得我们特别注意,因为他能通过编译没问题,但是计算结果错了而我们却丝毫不知。
其他类型
对于其它类型,如果支持运算符+, 那么它就和numeric类型一样,都可以使用std::accumulate.
std::string 的 + 运算符拼接了各个元素:
std::vector<std::string> words = { "Winter ", "is ", "Coming." };std::string sentence = std::accumulate(begin(words), end(words), std::string(""));
注意我们需要传递 std::string(""),作为初始值, 而不只是“”。因为后者所推导的T类型是 const char*,而不是std::string, 编译将通不过。
事实上,区间的元素类型没有实现运算符+,也可以使用std::accumulate 的第二个重写函数, 它将使用一个函数(或者函数对象)来代替运算符+。
这个函数的两个参数有可能还是不同的类型。 以下是相关的一个例子。
我们来考虑一个电梯,它能够承载若干人,但那是总的承载重量不能超过一个特定的阈值。以下代码计算电梯中一群人的总的重量:
double totalWeight = std::accumulate(begin(group), end(group), 0., [](double currentWeight, Person const& person) { return currentWeight + person.getWeight(); });请看算法中最后一个参数,它代表一个函数(这里是一个lambda表达式),这个函数的第一个参数(currentWeight)以第三个参数(这里是0.)初始化。 新的值不断的“吸收”到当前值。 当前值被“吸收",返回当前值, 或者当区间所有元素被处理,返回累加值。
std::accumulate的重写提供了很多可能性。但是其中有些要尽可能避免,因为它们使得代码像在用斧头在做分解,或者是用锯子。
我们将会给出一个例子,但在此之前,我们先提供一个原则:
“std::accumulate是对一个区间汇集成一个数的建模。
确实,想象下如果我们想要对电梯中的每个人进行称重。可以使用std::accumulate通过以下方式实现:
std::accumulate(begin(group),end(group), &weights,[](std::vector<double>*currentWeights, Person const& person){currentWeights->push_back(person.getWeight());returncurrentWeights;});但这样做是错误的。我曾经看到过别人这样的代码。见鬼,事实上我自己也曾经这么做过,当时对STLalgorithms还不是特别了解。
为什么这是错误的了?因为以上代码中,传入了一个区间,在区间的每个元素中使用了函数,最终结果生出了新的区间。事实上,这个功能应该使用std::transform去实现。
而且,以上代码扭曲了std::accumulate汇集一个区间为一个对象(一个值)的原则。
为了使以上代码更加具有意义。我们使用std::transform来实现该功能:
std::transform(begin(group),end(group), std::back_inserter(weights), [](Personconst& person){ return person.getWeight();});
你知道有这样一种现象:当你有一把锤子,你会觉得每件事都是指甲?同样的,使用accumulate去表达函数应用就像使用锤子去扫地。你花了很多时间去实现他,而你的邻居(后来者维护的dev)且因此而憎恨死你了。
想听一些类似accumulate误用的建议吗?
“如果std::accumulate的返回值不是一个对象(一个值)”,那么这是一个信号:accumulate不是我们想要的工具。
更多关于std::accumulate
以上这些都会让你对使用accumulate更有效率。但是不单单只有这些。
在关注BenDeane’s的CppCon讨论时 std::accumulate:Exploring an Algorithmic Empire.,我意识到这一点。
在文中,为了提起你的阅读兴趣,Ben展示了大量STL的算法可以被std::accumulate实现!同时,accumulate也可以用于实现std::all_of,但
还有更多的。
std::accumulate(std::begin(booleans),std::end(booleans), true, std::logical_and<>())
Accumulate是一个很有用的工具。使用它,但是当心。
原文:
https://www.fluentcpp.com/2017/10/17/stdaccumulate-your-knowledge-on-algorithms/
- STL算法——accumulate
- stl中的accumulate算法
- STL泛型算法--accumulate()
- STL算法 count_if函数 accumulate函数
- C++ STL算法系列3---求和:accumulate
- C++ STL算法系列3---求和:accumulate
- C++ STL算法系列3---求和:accumulate
- C++ STL算法之accumulate函数
- STL算法之accumulate函数学习
- C++ STL算法系列3---求和:accumulate
- C++ STL算法之accumulate函数
- C++ STL算法系列3---求和:accumulate
- C++ STL算法系列3---求和:accumulate
- STL-----------accumulate
- STL:accumulate
- STL accumulate
- 泛型算法之——accumulate和find_first_of
- accumulate算法
- NO.2 Spring MVC CRUD起步
- tensorflow+图像分类使用的一些错误
- Expect工具的安装及使用方法
- DOM数据绑定
- 无法表示的数 51Nod
- STL算法——accumulate
- HashMap
- Linux系统知识小结(五)
- Mac上安装boost库(在Xcode上)
- 666
- input输入框点击有边框的解决办法
- 卡尔曼滤波学习笔记
- 在java中使用任务调度定时进行运行环境检测
- 企业实战之Spring拦截器+注解实现《登录校验》