我有几个std::vector
,我需要一个接一个地迭代它们,以便我可以考虑第一组N个元素,然后第二组N个元素执行自定义操作,并且等等,直到最后一个向量的最后一个元素。
一种可行的方法是将每个向量复制到一个更大的向量中(带有2个输入向量的例子):
void foo(const vector<int>::iterator& it, const vector<int>::iterator& it2)
{
}
void iterate(const vector<int>& a, const vector<int>& b, int n)
{
vector<int> c = a;
c.insert(c.end(), b.begin(), b.end());
int i, j, len = c.size();
for (i = 0, j = 0; i < len; i++)
{
if (i > 0 && i % n == 0)
{
// custom operation from c.begin() + j to c.begin() + i
foo(c.begin() + j, c.begin() + i);
j = i;
}
}
if (i % n != 0)
{
// custom operation from c.begin() + j to c.end()
foo(c.begin() + j, c.end());
}
}
vector<int> a(100), b(50);
iterate(a, b, 32);
但是这种方法需要额外的矢量分配,可能很大。有没有办法尽可能快地执行相同的操作,而不需要额外的矢量?
答案 0 :(得分:2)
你可以使用向量数组而不是声明单独的向量,只需迭代它们。
vector<int> v[n];
vector<int>::iterator it;
for(i = 0; i < n; i++) {
for(it = v[i].begin(); it < v[i].end(); it++) {
// operation
}
}
答案 1 :(得分:2)
为什么不编写自己的迭代器?
迭代器(假设您可以将自己限制为所有可能的迭代器操作的子集),它只是一个提供所需操作的类。在您的情况下,您只需要实现++,==和*。
你可以像这样编写这个迭代器类:
VirtualCollection
的课程。VirtualCollection
添加一个成员,该成员是一个向量,指向要循环的基础数据结构(在您的情况下也是向量)。所有底层数据结构必须具有相同的类型(因此不能在vector
上循环,然后是list
,...)。让我们为这个答案的其余部分调用这个底层数据结构V
。我们称这个成员为m_collections
。addCollection
,您可以在其中传递对V
的引用。 Push_back指向V
m_collections
的指针
iterator
的内部类。
V::iterator
(或V::const_iterator
)类型的成员。拨打一个m_begin
和一个m_end
(或使用您自己喜欢的命名方式)。V::iterator
类型的成员,名为m_current
。VirtualCollection
添加一个开头,它返回iterator
内部类的实例。 m_begin
和m_end
应初始化为m_collections
中第一个向量的开头和结尾。 m_current
也应初始化为m_begin
。iterator
的*运算符。它应该只返回m_current
。iterator
来实现VirtualCollection::increment
的++运算符。此增量方法将递增m_current
。如果m_current
为m_end,则会在VirtualCollection
中的下一个集合的开头和结尾重新初始化所有3个成员。答案 2 :(得分:2)
使用range-v3,只需:
void iterate(const std::vector<int>& a, const std::vector<int>& b, int n)
{
auto r = ranges::view::concat(a, b) | ranges::view::chunk(n);
for (const auto& e : r | ranges::view::bounded) {
foo(e);
}
}
使用c ++ 17的for-range。
答案 3 :(得分:0)
您可以构造一个指向输入向量的临时向量,并在循环中使用它们进行操作
// not tested
void iterate(const vector<int>& a, const vector<int>& b, int n)
{
vector<const vector<int> *> c;
c.push_back(&a);
c.push_back(&b);
for(auto vec : c)
{
//operate on (*vec), which are a and b
}
}
答案 4 :(得分:0)
您不能混合来自两个不同容器的迭代器。因此,当您从第一个容器跳到第二个容器时,这不会起作用。
如果您主要关注的是分配额外的向量,则可以在适当的位置工作,并且仅在从第一个向量到第二个向量时分配临时向量。这会将额外的内存需求减少到n
。
void iterate(const std::vector<int> &a, const std::vector<int> &b, int n)
{
int remain = a.size();
for (int i = 0; remain >= n; remain -= n, i += n) {
foo(a.begin() + i, a.begin() + i + n);
}
if (remain > 0) {
std::vector<int> tmp;
tmp.insert(tmp.end(), a.end() - remain, a.end());
tmp.insert(tmp.end(), b.begin(), b.begin() + n - remain);
foo(tmp.begin(), tmp.end());
}
int start = n - remain;
remain = b.size() - start;
for (int i = start; remain >= n; remain -= n, i += n) {
foo(b.begin() + i, b.begin() + i + n);
}
if (remain > 0)
foo(b.end() - remain, b.end());
}
如果您不关心每次都准确地执行n
元素,您也可以只处理一个向量及其余向量,然后处理下一个向量
void iterate(const std::vector<int> &v, int n)
{
int remain = v.size();
for (int i = 0; remain >= n; remain -= n, i += n)
foo(v.begin() + i, v.begin() + i + n);
if (remain > 0)
foo(v.end() - remain, v.end());
}
std::vector<int> a(100), b(50);
iterate(a, 32);
iterate(b, 32);
答案 5 :(得分:0)
您可以像这样使用std :: reference_wrapper:
static int val = 1;
void foo(const vector<int>::iterator& it, const vector<int>::iterator& it2)
{
std::for_each(it, it2, [](int& v) {v = val; });
val++;
}
void iterate(const vector<std::reference_wrapper<vector<int>>>& data, vector<int>::size_type n)
{
vector<int>::size_type tmp_beg = 0;
vector<int>::size_type tmp_end = n;
for (std::reference_wrapper<vector<int>> vref : data)
{
vector<int>& v = vref.get();
while (tmp_end < v.size())
{
foo(v.begin() + tmp_beg, v.begin() + tmp_end);
tmp_beg = tmp_end;
if (tmp_beg >= v.size()) {
tmp_beg -= v.size();
}
tmp_end = tmp_beg + n;
} //tmp_end >= v.size()
foo(v.begin() + tmp_beg, v.end());
val--; //We need the same value again....
tmp_beg = 0;
tmp_end -= v.size();
if (tmp_end == 0) {
tmp_end = n;
}
}
}
int main()
{
vector<int> a(100), b(50);
iterate({ a,b }, 32);
return 0;
}