采用测试和变异函数的STL算法
STL Algorithm that Takes a Test and Mutate Function
我想要的是这种行为:void change_if( ForwardIterator first, ForwardIterator last, UnaryPredicate test, UnaryOperation op )
实现这一点的最佳方法是使用for循环吗?还是有一些我还不知道的STL魔法?
这可以在不使用boost但应用标准算法std::for_each
的情况下完成我不建议在这样简单的任务中使用boost。在你的项目中加入boost来执行这样一项简单的任务简直太愚蠢了。如果boost已经包含在您的项目中,您可以将其用于此类任务。
std::for_each( first, last, []( const T &x ) { if ( test( x ) ) op( x ); } );
或者,如果要更改序列的元素,可以删除限定符const
std::for_each( first, last, []( T &x ) { if ( test( x ) ) op( x ); } );
有时,当使用序列的整个范围时,使用基于范围的for语句而不是算法会更简单,因为使用带有lambda表达式的算法有时会降低代码的可读性
for ( auto &x : sequence )
{
if ( test( x ) ) op( x );
}
或
for ( auto &x : sequence )
{
if ( test( x ) ) x = op( x );
}
Vlad来自莫斯科的解决方案是推荐的简单方法。
std::transform
标准算法与lambda:的"看似可怕"的使用
std::transform(first, last, first, [](auto elem) {
return test(elem) ? op(elem) : elem;
});
实际上会导致性能下降,因为所有元素都将被分配给,而不仅仅是那些满足谓词的元素。要只修改谓词元素,还需要像kiwi在回答中提到的boost::filter_iterator
一样的东西。
注意,我在lambda中使用了带有auto
的C++14语法。对于C++11,您需要类似decltype(*first)
或iterator_traits<ForwardIterator>::value_type
的东西。在C++98/03中,您将同时使用它和手工制作的函数对象。
还有另一个提升解决方案:
http://www.boost.org/doc/libs/1_55_0/libs/iterator/doc/filter_iterator.html
只需在过滤迭代器上调用std::transform。
std::for_each( first, last, []( T &x ) { if ( test( x ) ) op( x ); } );
或使用增强lambda:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <boost/lambda/if.hpp>
std::for_each( v.begin(), v.end(),
if_( test() )[ op() ]
);
或者:
std::vector<int>::iterator it = v.begin();
while ( it != v.end()) {
if ( test( *it)) op(*it);
++it;
}
您想要change_if
作为一个简单的循环吗?
template<typename ForwardIterator, typename UnaryPredicate>
void change_if( ForwardIterator first, ForwardIterator last, UnaryPredicate test, UnaryOperation op ) {
for(; first!=last; ++first)
if (test(*first)) *first=op(std::move(*first));
}
或者只写上面的循环。我建议实际编写change_if
并调用它,因为虽然上面的代码很短,但我会发现change_if
调用比直接将上面的代码放入更清晰
我也喜欢写基于容器的重载:
template<typename Container, typename UnaryPredicate>
void change_if( Container&& c, UnaryPredicate test, UnaryOperation op ) {
for(auto& v : std::forward<Container>(c))
if (test(v)) v=op(std::move(v));
}
但我也有这个:
template<typename Iterator>
struct range {
Iterator b, e;
Iterator begin() const { return b; }
Iterator end() const { return e; }
};
template<typename Iterator0, typename Iterator1>
range<typename std::decay<Iterator0>::type> make_range(Iterator0&& b, Iterator1&& e) {
static_assert(
std::is_convertible< Iterator1, typename std::decay<Iterator0>::type >::value,
"end must be compatible with begin iterator type"
);
return { std::forward<Iterator0>(b), std::forward<Iterator1>(e) };
}
这让我可以将这种基于容器的算法与迭代器一起使用。
你会看到我有一个基于Container
的change_if
吗?它实际上是一个基于范围的change_if
。
它被称为:
change_if( myVect, [](int x){return (x%2)==0;}, [](int x){return x/2;} );
在容器上,而不是在一对迭代器上。然而,如果你只想更改容器的前半部分,那就行不通了:所以乍一看,基于容器(好吧,基于范围)的算法用处不大。
但是make_range
将迭代器变成了一个范围。所以你可以:
change_if( make_range( myVec.begin(), myVec.begin()+myVec.size()/2 ), [](int x){return (x%2)==0;}, [](int x){return x/2;} )
CCD_ 15通过将两个迭代器绑定到一个CCD_。这个角落的情况更详细,但典型的情况(处理整个容器)变得不那么详细。
此外,一种常见的错误(为begin
和end
命名不同的容器)也变得不那么频繁了。
所有这些最终都与基于迭代器的版本一样高效,甚至更高效。而且,如果您将范围替换为可迭代项(具有不同begin
和end
迭代器类型的范围),则我的change_if
只适用于
- 为什么这个运算符<重载函数对 STL 算法不可见?
- STL算法函数在多个一维容器上的使用
- C++:尝试使用等效的 STL 算法消除原始循环
- STL算法和back_inserter可以预分配空间吗?
- 如何使用 STL 算法将整数向量转换为字符串向量?
- 是否有一种 STL 算法可以最后找到,但它也适用于指针?
- C++ - 如何使用<int> <int>STL 算法功能在矢量<vector>中找到矢量?
- 如何在变量容器上使用stl算法
- 在 stl 算法中移动迭代器
- 如何使用 STL 算法找到最小值和最大值?
- C STL算法相等
- 在 stl 算法中使用函数对象
- 重新定义要在字符串的 STL 算法中使用的<运算符
- 将 STL 算法与 Qt 容器结合使用
- 使用STL算法合并2个向量
- STL算法函数,如累加,如果传递给它们的函数接受引用,请避免复制
- 什么 STL 算法可以确定容器中的一个项目是否满足谓词?
- c ++ 是否有 STL 算法来检查范围是否严格排序
- begin() 和 end() 用于 STL 算法
- STL算法并嵌套以循环