【问题标题】:Idiomatic STL: Iterating over a list and inserting elements惯用 STL:遍历列表并插入元素
【发布时间】:2010-04-07 20:19:38
【问题描述】:

我正在编写一个算法,它遍历点列表,计算它们之间的距离,如果距离太大,则插入额外的点。然而,我似乎缺乏对 STL 的适当熟悉来提出一个优雅的解决方案。我希望我能学到一些东西,所以我将向您展示我的代码。您可能对我有一些提示。

for (std::list<PathPoint>::iterator it = ++points_.begin();
     it != points_.end(); it++)
{
    Vector curPos = it->getPosition();
    Vector prevPos = (--it)->getPosition();
    Vector vecFromPrev = curPos - prevPos;
    float distance = vecFromPrev.abs();
    it++;
    if (distance > MAX_DISTANCE_BETWEEN_POINTS)
    {               
        int pointsToInsert = (int)(distance / MAX_DISTANCE_BETWEEN_POINTS);             
        Vector curPos = prevPos;                
        for (int i = 0; i < pointsToInsert; i++)
        {
            curPos += vecFromPrev / pointsToInsert;
            it = points_.insert(it, PathPoint(curPos, false));
            it++;
        }
    }
}

【问题讨论】:

  • 我觉得不错。虽然我会做一些事情来明确Vector 不是std::vector&lt;t&gt;
  • 您将it 增加两次,但未检查它是否有效。
  • 其实我不是。在循环内部,我的迭代器将始终指向有效的列表元素。然而,迭代器的移动有点疯狂,这也是我对我的代码不太满意的原因之一。
  • 在循环定义中,您在不知道其有效性的情况下执行一次。如果您的列表为空,++points_.begin() 就会爆炸。

标签: c++ stl iterator


【解决方案1】:

考虑使用adjacent_find查找连续元素之间距离过大的迭代器位置,然后插入pointsToInsert项。

http://www.sgi.com/tech/stl/adjacent_find.html

此外,您可以使用generate 和函子来填充中间点。

http://www.sgi.com/tech/stl/generate.html

不确定你想深入了解 STL :)

【讨论】:

  • 学习的好方法,但广泛使用 stl 会给出相当不可读的代码,除非读者非常熟悉 stl。
  • 无论你采用什么方法,写得不好的代码都很难阅读。
【解决方案2】:

您的迭代解决方案完全可以理解。我知道当你说“我希望我能学到一些东西”时,这不是你的本意,但我希望你学到的是:

1) 为您以良好的方式迭代解决的问题找到“优雅”的功能解决方案没有任何好处

2) C++ 中的函数式编程是乏味的,甚至比 C++ 已经乏味的还要多。

【讨论】:

    【解决方案3】:

    我不喜欢提及迭代器类型,因为 1.) 它们有点难看,并且 2.) 如果我更改集合类型,它会减少我必须进行的更改,所以我可能会做这样的事情.. ..

    我做了一些额外的调整,可能更多的是我的个人风格而不是惯用的。

    this->addAdditionalPoints(points.begin(), points.end());
    
    
    template<typename InIt>
    void MyClass::addAdditionalPoints(InIt start, InIt finish)
    {
       InIt it = start;
       ++it;                                      // Starting with second element
       for (; it != finish; ++it)  // I usually pre-increment iterators, but 
                                                 // it probably doesn't matter.
       {
          InIt curr = it;                       // Work with a temp rather than loop index
          Vector curPos = curr->getPosition();
          Vector prevPos = (--curr)->getPosition();
          Vector vecFromPrev = curPos - prevPos;
          float distance = vecFromPrev.abs();
          ++curr;                             // Prefer to pre-increment iterators
          if (distance > MAX_DISTANCE_BETWEEN_POINTS)
          {               
              int pointsToInsert = static_cast<int>(distance /              
                  MAX_DISTANCE_BETWEEN_POINTS);  // I prefer C++-style casts     
              Vector curPos = prevPos;                
              for (int i = 0; i < pointsToInsert; i++)
              {
                  curPos += vecFromPrev / pointsToInsert;
                  curr = points_.insert(curr, PathPoint(curPos, false));
                  ++curr;  // Again I prefer to pre-increment iterators
              }
          }
       }
    }
    

    【讨论】:

      【解决方案4】:

      您不必将列表插入的返回值捕获到迭代器。这样,您就不需要手动增加它。

      for (int i = 0; i < pointsToInsert; i++)
      {
          curPos += vecFromPrev / pointsToInsert;
          points_.insert(it, PathPoint(curPos, false));
      }
      

      【讨论】:

        【解决方案5】:

        Stephen 的解决方案不错,但为了教育起见,您可以一次循环两个变量:

        typedef typename std::list<PathPoint>::iterator Itr; //Pointless, but just to illustrate the possibility
        for(Itr cur = points_.begin(), prev = cur++; cur != points_.end(); ++prev, ++cur) {
            Vector curPos = cur->getPosition();
            Vector prevPos = prev->getPosition();
            Vector vecFromPrev = curPos - prevPos;
            float distance = vecFromPrev.abs();
            if (distance > MAX_DISTANCE_BETWEEN_POINTS) {               
                int pointsToInsert = (int)(distance / MAX_DISTANCE_BETWEEN_POINTS);             
                Vector curPos = prevPos;                
                for (int i = 0; i < pointsToInsert; i++) {
                    curPos += vecFromPrev / pointsToInsert;
                    prev = points_.insert(cur, PathPoint(curPos, false));
                    //as somebody mentioned, `cur` remains valid during `list` insertions
                }
            }
        }
        

        像这样来回移动迭代器有点令人困惑。另外,请注意,这和您的原始代码都不太喜欢空列表。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-06
          • 1970-01-01
          • 2014-04-12
          • 2017-08-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多