如何解决C++ - 将双精度向量作为元组迭代
我有一个双精度的 C++ 向量,它保证有偶数个元素。这个向量将一组点的坐标存储为 x,y 坐标:
A[2 * i ] 是第 i 个点的 x 坐标。
A[2 * i + 1] 是第 i 个点的 y 坐标。
如何实现允许我使用 STL 风格算法的迭代器(一个采用迭代器范围的算法,其中取消引用迭代器会返回对应于对应点的 x、y 坐标的一对双精度值)?
如果有帮助,我将使用 C++17。
解决方法
我们可以使用带有两个适配器的 range-v3:
-
chunk(n)
获取一个范围并将其调整为大小为n
的非重叠范围的范围
-
transform(f)
取范围x
并将其调整为范围f(x)
将它们放在一起,我们可以创建一个适配器,它接受一个范围并产生一系列非重叠对:
auto into_pairs = rv::chunk(2)
| rv::transform([](auto&& r){ return std::pair(r[0],r[1]); });
使用 r[0]
语法假定输入范围是随机访问的,在这种情况下这很好,因为我们知道我们想在 vector
上使用它,但它也可以推广为以更多语法为代价为只进范围工作:
| rv::transform([](auto&& r){
auto it = ranges::begin(r);
auto next = ranges::next(it);
return std::pair(*it,*next);
})
Demo,使用 fmt
方便打印:
int main() {
std::vector<int> v = {1,1,2,3,4,5,5};
auto into_pairs = rv::chunk(2)
| rv::transform([](auto&& r){ return std::pair(r[0],r[1]); });
// prints {(1,1),(2,2),(3,3),(4,4),(5,5)}
fmt::print("{}\n",v | into_pairs);
}
从问题中不清楚您是想要 pair<T,T>
还是 pair<T&,T&>
。后者可以通过向 std::pair
提供显式类型而不是依赖类模板参数推导来实现。
C++ 有点像一个移动目标——在迭代器方面也是如此(c++20 有这方面的概念......)。但是对问题有一个懒惰的解决方案不是很好。 IE。元组是即时生成的,无需强制转换(请参阅其他答案),也无需编写循环将 vector<double>
转换为 vector<tuple<double,double>>
?
现在我觉得我需要免责声明,因为我不确定这是否完全正确(如果我遗漏了什么,语言律师希望能指出)。但它编译并产生预期的输出。那是什么,是吗?!是的。
我们的想法是构建一个带有自己的迭代器的伪容器(实际上只是底层容器的外观),即时生成所需的输出类型。
#include <vector>
#include <tuple>
#include <iostream>
#include <iterator>
template <class SourceIter>
struct PairWise {
PairWise() = delete;
PairWise(SourceIter first,SourceIter last)
: first{first},last{last}
{
}
using value_type =
typename std::tuple<
typename SourceIter::value_type,typename SourceIter::value_type
>;
using source_iter = SourceIter;
struct IterState {
PairWise::source_iter first;
PairWise::source_iter last;
PairWise::source_iter current;
IterState(PairWise::source_iter first,PairWise::source_iter last)
: first{first},last{last},current{first}
{
}
friend bool operator==(const IterState& a,const IterState& b) {
// std::cout << "operator==(a,b)" << std::endl;
return (a.first == b.first)
&& (a.last == b.last)
&& (a.current == b.current);
}
IterState& operator++() {
// std::cout << "operator++()" << std::endl;
if (std::distance(current,last) >= 2) {
current++;
current++;
}
return *this;
}
const PairWise::value_type operator*() const {
// std::cout << "operator*()" << std::endl;
return std::make_tuple(*current,*(current+1));
}
};
using iterator = IterState;
using const_iterator = const IterState;
const_iterator cbegin() const {
return IterState{first,last};
}
const_iterator cend() const {
auto i = IterState{first,last};
i.current = last;
return i;
}
const_iterator begin() const {
// std::cout << "begin()" << std::endl;
return IterState{first,last};
}
const_iterator end() const {
// std::cout << "end()" << std::endl;
auto i = IterState{first,last};
i.current = last;
return i;
}
source_iter first;
source_iter last;
};
std::ostream& operator<<(std::ostream& os,const std::tuple<double,double>& value) {
auto [a,b] = value;
os << "<" << a << "," << b << ">";
return os;
}
template <class Container>
auto pairwise( const Container& container)
-> PairWise<typename Container::const_iterator>
{
return PairWise(container.cbegin(),container.cend());
}
int main( int argc,const char* argv[]) {
using VecF64_t = std::vector<double>;
VecF64_t data{ 1.0,2.0,3.0,4.0,5.0,6.0 };
for (const auto x : pairwise(data)) {
std::cout << x << std::endl;
}
return 0;
}
,
vector 中的元素存储在连续的内存区域中,因此您可以使用简单的指针算法来访问双精度对。
operator++
迭代器在每次使用时应该跳过 2 个双打。
operator*
可以返回对双精度值的引用元组,因此您可以读取 (pair = *it
) 或编辑值 (*it = pair
)。
struct Cont {
std::vector<double>& v;
Cont(std::vector<double>& v) : v(v) {}
struct Iterator : public std::iterator<std::input_iterator_tag,std::pair<double,double>> {
double* ptrData = nullptr;
Iterator(double* data) : ptrData(data) {}
Iterator& operator++() { ptrData += 2; return *this; }
Iterator operator++(int) { Iterator copy(*this); ptrData += 2; return copy; }
auto operator*() { return std::tie(*ptrData,*(ptrData+1)); }
bool operator!=(const Iterator& other) const { return ptrData != other.ptrData; }
};
auto begin() { return Iterator(v.data()); }
auto end() { return Iterator(v.data()+v.size());}
};
int main() {
std::vector<double> v;
v.resize(4);
Cont c(v);
for (auto it = c.begin(); it != c.end(); it++) {
*it = std::tuple<double,double>(20,30);
}
std::cout << v[0] << std::endl; // 20
std::cout << v[1] << std::endl; // 30
}
,
没有一种简单的“C++”方法可以做到这一点,既干净又避免原始数组的副本。总有这个(复制一份):
vector<double> A; // your original list of points
vector<pair<double,double>> points;
for (size_t i = 0; i < A.size()/2; i+= 2)
{
points[i*2] = pair<double,double>(A[i],A[i+1]);
}
以下内容可能会奏效,但违反了一些标准,语言律师会因为我的建议而在法庭上起诉我。但是如果我们可以假设 sizeof(XY)
是两个双打的大小,没有填充,并且预期对齐,那么使用演员表作弊可能会奏效。这假设您不需要 std::pair
非标准的东西
vector<double> A; // your original list of points
struct XY {
double x;
double y;
};
static_assert(sizeof(double)*2 == sizeof(XY));
static_assert(alignof(double) == alignof(XY));
XY* points = reinterpret_cast<XY*>(A.data());
size_t numPoints = A.size()/2;
// iterate
for (size_t i = 0; i < numPoints; i++) {
XY& point = points[i];
cout << point.x << "," << point.y << endl;
}
,
如果你能保证 std::vector 总是有偶数个条目,你可以利用这样一个事实,即双精度向量与双精度向量具有相同的内存布局。不过,这是一个肮脏的把戏,所以如果你能避免的话,我不会推荐它。好消息是该标准保证了向量元素是连续的。
__call__
这仅适用于迭代器的迭代。大小很可能是向量中对数的两倍,因为它下面仍然是一个双数向量。
示例:
inline const std::vector<std::pair<double,double>>& make_dbl_pair(std::vector<double>& v)
{
return reinterpret_cast<std::vector<std::pair<double,double>>&>(v);
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。