【问题标题】:reverse iterating and comparing two consecutive elements of std::vector (c++)反向迭代和比较 std::vector (c++) 的两个连续元素
【发布时间】:2020-05-10 16:54:17
【问题描述】:

我正在遍历定义圆(闭合形状)的点列表。对于每个点,我都会检查这一点和下一点之间的距离,如果距离大于 4,则决定细分。

for (vector<Node>::reverse_iterator i = nodes.rbegin(); i != nodes.rend(); ++i) // going through list in reverse
{
    ofVec3f v1 = i->position;
    i += 1;
    ofVec3f v2 = i->position;
    i -= 1;

    float distance = v1.distance(v2);
    if (distance > 4) {
        interpolate(v1, v2);
    }
}

但问题是,我希望最后一次迭代的 i + 1 元素是列表的第一个元素(因为它是一个封闭的形状)。我曾经在 c# 中有一个非常方便的函数,但由于迭代器的工作方式,我无法使用它。

int DiffLine::idxFix(int i) {
int c = nodes.size();
if (i < 0) { return c - 1; }
else { return i % c; }
}

如何在我的 C++ 场景中实现类似的功能?

【问题讨论】:

  • aww 废话我错过了一个 i- 在那个循环的末尾,让我编辑它。我正在有效地尝试测试 p5 p4、p4 p3、p3 p2 ...

标签: c++ vector iterator


【解决方案1】:

检查i + 1。如果是rend(),则从rbegin() 获取v2。你说你的结构是一个“列表”,但是你使用+= 告诉我你有随机访问迭代器,所以我写了下面假设你有一个随机访问迭代器(比如如果nodesstd::vector) .如果不是这种情况(例如,如果 nodes 实际上是 std::list),则需要稍作修改。

for (auto i = nodes.rbegin(); i != nodes.rend(); ++i) // going through list in reverse
{

    ofVec3f v1 = i->position;
    ofVec3f v2;
    auto j = i + 1;
    if (j == nodes.rend()) {
        v2 = nodes.rbegin()->position;
    }
    else
    {
        v2 = j->position;
    }

    float distance = v1.distance(v2);
    if (distance > 4) {
        interpolate(v1, v2);
    }
}

当然,您不必必须使用迭代器。如果这是一个随机访问迭代器(再次假设nodes 是一个std::vector),您可以使用一个good-ol' indexed for 循环:

for (std::size_t i = nodes.size(); i != 0; --i)
{
    ofVec3f v1 = nodes[i % nodes.size()].position;
    ofVec3f v2 = nodes[i - 1].position;

    float distance = v1.distance(v2);
    if (distance > 4) {
        interpolate(v1, v2);
    }
}

请注意,上面的代码将在第一次迭代时检查 FIRST 元素和 LAST 元素。如果这不可接受,那么我们可以进行一些更改。

【讨论】:

  • 非常感谢,我今天开始使用 c++,它与我以前做过的任何事情都完全不同。第一个解决方案似乎工作正常,但由于我无法解释的原因引发了一些矢量错误。好的 ol' indexed for 循环的工作原理就像一个魅力,并且真的让人放心看到我熟悉的东西。为什么这些不经常使用?
  • iterators 的推送被认为是更加面向对象的——将物品容器视为物品容器(例如一桶苹果),而不是装配线上的编号物品。当随机访问不起作用时,迭代器可以与所有&lt;algorithm&gt; 函数一起使用。然后你可以解耦你的算法和你的容器。对std::vector 进行排序的算法也会对std::list 进行排序,即使您不能对my_list[5] 之类的列表进行索引。迭代器也可能更快,但大多数编译器应该优化掉这种好处。
  • 糟糕,发现错误。我设置了v2 = nodes.begin()-&gt;position,而它应该是v2 = nodes.rbegin()-&gt;position。您是否有任何理由反向迭代与正向迭代?
  • 您在循环中向std::vector 添加元素?向量将其元素存储在连续的内存块中。如果向量变得太大,则需要将所有元素移动到新内存中。因此,向向量中添加元素可能会使它的迭代器失效。这是迭代器代码问题的潜在原因。
  • 是的,类似于“工作队列”或缓冲区。但是你需要存储“对这个向量做工作”队列的概念,而且没有人有时间做这个。
【解决方案2】:

您可以为随机访问迭代器实现包装器,它执行与 c# 方法类似的操作:

#include <vector>
#include <iostream>

template <typename RandomAccessIterator>
class LoopWrapper {
 public:
  LoopWrapper(const RandomAccessIterator& first,
              const RandomAccessIterator& last)
      : first_(first), dist_(last-first) {}
  typename RandomAccessIterator::value_type& operator*() {
    return *(first_ + pos_);
  }
  LoopWrapper& operator++() {
    pos_++;
    if (pos_ == dist_) {
      pos_ = 0;
      wrapped_ = true;
    }
    return *this;
  }
  LoopWrapper& operator--() {
    pos_--;
    if (pos_ < 0) pos_ = dist_ - 1;
    return *this;
  }
  LoopWrapper operator+(int offset) {
    LoopWrapper result = *this;
    result.pos_ += offset;
    result.pos_ %= dist_;
    if (result.pos_ < 0) result.pos_ += dist_;
    return result;
  }
  LoopWrapper operator-(int offset) {
    LoopWrapper result = *this;
    result.pos_ -= offset;
    result.pos_ %= dist_;
    if (result.pos_ < 0) result.pos_ += dist_;
    return result;
  }
  operator RandomAccessIterator() { return first_ + pos_; }
  bool Done() const { return wrapped_; }

 private:
  const RandomAccessIterator first_;
  const int dist_;
  int pos_ = 0;
  bool wrapped_ = false;
};

int main() {
  std::vector<int> v{1, 2, 3};
  for (auto i = LoopWrapper(v.begin(), v.end()); !i.Done(); ++i) {
    std::cout << *i << ", " << *(i + 1) << std::endl;
  }
  return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-20
    • 1970-01-01
    • 2013-08-29
    • 2016-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-25
    相关资源
    最近更新 更多