如何从传递给STL算法的谓词中获取元素的索引
How to obtain index of element from predicate passed to some STL algorithm?
比方说,我有一个元素的向量和一个掩码数组,我想从具有真正对应掩码值的向量中提取元素,以分离向量。有没有办法将std::copy_if
用于此目的?问题是,我在谓词中只有元素的值,而没有迭代器的值,所以我无法知道地址掩码数组的实际索引。
我可以直接操作这样的地址:
vector<bool> mask;
vector<int> a, b;
copy_if(a.begin(), a.end(), b.begin(), [&] (int x) -> bool {
size_t index = &x - &a[0]; // Ugly...
return mask[index];
});
然而,我发现这是一个丑陋的解决方案。有更好的主意吗?
更新:另一个可能的解决方案是在掩码上使用外部迭代器:
vector<bool> mask;
vector<int> a, b;
auto pMask = mask.begin();
copy_if(a.begin(), a.end(), b.begin(), [&] (int x) {
return *pMask++;
});
然而,此解决方案需要在外部命名空间中添加额外的变量,这仍然是不可取的。
好的,经过一番调查,我得出了第一个例子,这是最简单的方法。然而,不应该忘记通过(const)引用传递lambda中的值,因为它不采用参数的本地副本的地址:
copy_if(a.begin(), a.end(), b.begin(),
[&] (const int& x) -> bool { // <-- do not forget reference here
size_t index = &x - &a[0]; // Still ugly... but simple
return mask[index];
});
我的答案:
vector<bool> mask ;
vector<int> a, b;
auto it = std::copy_if (a.begin(), a.end(), b.begin(), [&, index = 0] (const int x) mutable -> bool {
return mask[index++]; // increment index
});
这使用状态完整的lambda。index只设置为零一次,每次使用时都会递增。编辑:需要c++14
您可以将几个迭代器组合成Boost(没有经过真正的测试,但使用GCC 4.6编译):
#include <algorithm>
#include <boost/iterator/counting_iterator.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/tuple/tuple.hpp>
int main() {
std::vector<bool> mask;
std::vector<int> a, b;
boost::counting_iterator<size_t> count_begin(0), count_end(a.size());
auto zip_begin = boost::make_zip_iterator(boost::make_tuple(count_begin, a.begin()));
auto zip_end = boost::make_zip_iterator(boost::make_tuple(count_end, a.end()));
typedef decltype(zip_end) zip_iterator;
typedef const zip_iterator::value_type& zip_value;
auto pred = [&mask](zip_value val) {
auto index = val.get<0>();
return index < mask.size() ? mask[index] : true;
};
auto filter_begin = boost::make_filter_iterator(pred, zip_begin, zip_end);
auto filter_end = boost::make_filter_iterator(pred, zip_end, zip_end);
std::transform(filter_begin, filter_end, back_inserter(b), [](zip_value val) {
return val.get<1>();
});
}
然而,我认为显式循环在这里更简单。
这是上面代码的另一个更通用的版本,这次甚至经过了测试:)它提供了类似于map
、filter
和enumerate
函数的Python实现。此项要求GCC 4.7。
#include <utility>
#include <vector>
#include <iterator>
#include <type_traits>
#include <iostream>
#define BOOST_RESULT_OF_USE_DECLTYPE
#include <boost/tuple/tuple.hpp>
#include <boost/iterator/zip_iterator.hpp>
#include <boost/iterator/filter_iterator.hpp>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/range/begin.hpp>
#include <boost/range/end.hpp>
#include <boost/range/size.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/range/counting_range.hpp>
#include <boost/range/algorithm/copy.hpp>
#include <boost/range/algorithm_ext/push_back.hpp>
template<typename... ForwardRange>
using zip_range = boost::iterator_range<
boost::zip_iterator<
boost::tuple<
typename boost::range_iterator<
typename std::remove_reference<ForwardRange>::type>::type...>>>;
template<typename... ForwardRange>
zip_range<ForwardRange...>
zip(ForwardRange&&... ranges) {
return boost::make_iterator_range(
boost::make_zip_iterator(
boost::make_tuple(
boost::begin(std::forward<ForwardRange>(ranges))...)),
boost::make_zip_iterator(
boost::make_tuple(
boost::end(std::forward<ForwardRange>(ranges))...)));
}
template<typename ForwardRange, typename Index>
using enumerating_range = zip_range<
boost::iterator_range<boost::counting_iterator<Index>>,
ForwardRange>;
template<typename ForwardRange, typename Index>
enumerating_range<ForwardRange, Index>
enumerate(ForwardRange&& range, Index start) {
return zip(
boost::counting_range(
start,
static_cast<Index>(start + boost::size(range))),
std::forward<ForwardRange>(range));
}
template<typename Predicate, typename ForwardRange>
using filter_range = boost::iterator_range<
boost::filter_iterator<
Predicate,
typename boost::range_iterator<
typename std::remove_reference<ForwardRange>::type>::type>>;
template<typename Predicate, typename ForwardRange>
filter_range<Predicate, ForwardRange>
filter(Predicate pred, ForwardRange&& range) {
return boost::make_iterator_range(
boost::make_filter_iterator(
pred,
boost::begin(std::forward<ForwardRange>(range))),
boost::make_filter_iterator(
pred,
boost::end(std::forward<ForwardRange>(range))));
}
template<typename UnaryOperation, typename ForwardRange>
using map_range = boost::iterator_range<
boost::transform_iterator<
UnaryOperation,
typename boost::range_iterator<
typename std::remove_reference<ForwardRange>::type>::type>>;
template<typename UnaryOperation, typename ForwardRange>
map_range<UnaryOperation, ForwardRange>
map(UnaryOperation operation, ForwardRange&& range) {
return boost::make_iterator_range(
boost::make_transform_iterator(
boost::begin(std::forward<ForwardRange>(range)),
operation),
boost::make_transform_iterator(
boost::end(std::forward<ForwardRange>(range)),
operation));
}
template<typename UnaryOperation, typename Predicate, typename ForwardRange>
using filter_map_range = map_range<
UnaryOperation,
filter_range<Predicate, ForwardRange>>;
template<typename UnaryOperation, typename Predicate, typename ForwardRange>
filter_map_range<UnaryOperation, Predicate, ForwardRange>
filter_map(UnaryOperation operation, Predicate pred, ForwardRange&& range) {
return map(operation, filter(pred, range));
}
int main() {
std::vector<int> a { 10, 11, 12, 13, 14 };
std::vector<bool> mask { false, true, true, false, true };
std::vector<int> b;
auto enumerator = enumerate(a, 0u);
typedef boost::range_value<decltype(enumerator)>::type enum_value;
boost::push_back(
b,
filter_map(
[](const enum_value& val) {
return val.get<1>();
},
[&mask](const enum_value& val) {
auto i = val.get<0>();
return i < mask.size() ? mask[i] : true;
},
enumerator));
boost::copy(b, std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
如果你不需要使用矢量,解决方案会变得有些无聊:
#include <valarray>
#include <algorithm>
#include <iterator>
#include <iostream>
int main() {
using namespace std;
valarray<int> a { 10, 11, 12, 13, 14 };
valarray<bool> mask { false, true, true, false, true };
valarray<int> b = a[mask];
copy(begin(b), end(b), ostream_iterator<int>(cout, " "));
}
相关文章:
- C++为构建时间获取QDateTime的可靠方法
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 如何使用 < 和 > 命令获取 c++ 中的输入和输出?
- 使用指针从C++中的数组中获取最大值
- std::condition_variable::wait()如何评估给定的谓词
- 如何获取std::result_of函数的返回类型
- 如何在openssl-ecc中获取十六进制格式的私钥
- 使用Unreal C++获取VR耳机的世界位置/方向
- 获取日期异步信号安全吗?如果在信号处理程序中使用,它会导致死锁吗
- 从C字符串中获取奇怪的字符串长度
- 为什么我的for循环不能正确获取argv
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- 从python中调用C++函数并获取返回值
- 如何获取一个数字的前3位
- 获取字符串的长度并将其分配给数组
- 无法获取菜单选择以运行函数.C++
- 为什么 std::find_if(first, last, p) 不通过引用获取谓词?
- 从满足谓词的迭代器中均匀随机的容器中获取迭代器
- 如何获取与某个谓词匹配的类型的索引
- 如何从传递给STL算法的谓词中获取元素的索引