【问题标题】:Using iterators in nested loop through two vectors to match elements and erase from the vectors在嵌套循环中使用迭代器通过两个向量来匹配元素并从向量中擦除
【发布时间】:2019-04-29 03:39:00
【问题描述】:

我想使用以下循环来匹配两个向量中的元素,然后从向量中删除它们:

for(auto it1=left_unprocessed_event_arrays.begin(); it1!=left_unprocessed_event_arrays.end(); ++it1){
  for(auto it2=right_unprocessed_event_arrays.begin(); it2!=right_unprocessed_event_arrays.end(); ++it2){
    if(it1->header.stamp.nsec==it2->header.stamp.nsec){
      matching_event_arrays.push_back({*it1,*it2});
      left_unprocessed_event_arrays.erase(it1);
      right_unprocessed_event_arrays.erase(it2);
    }
  }
}

然后我意识到我不能这样做,因为使用 erase() 会使迭代器无效。
搜索解决方案将我带到this。这里有人建议使用erase()返回的指针,然后像这样递增else-bracket中的迭代器:

std::vector<std::string>::iterator iter;
for (iter = m_vPaths.begin(); iter != m_vPaths.end(); ) {
    if (::DeleteFile(iter->c_str()))
        iter = m_vPaths.erase(iter);
    else
        ++iter;
}

但是当我只在内循环中增加时,它不会正确地通过外循环。我正在努力了解如何使这项工作适用于我的嵌套循环。
所以我的问题是:如何实现嵌套循环的链接答案的解决方案?或者如果有另一个/更好的解决方案:那是什么?

【问题讨论】:

  • 您是否考虑过使用索引从结尾迭代到开头?这样,您的删除将永远不会影响您所在的位置。人们也可能会争辩说它会更快,因为你会移动更少的东西。此外,如果您可以以某种方式对对象进行排序,而不是具有 O(n²) 复杂性,那么您只需要每个向量上的当前索引/迭代器,这将更接近 O(n)
  • @AlexG 它如何不影响我在哪里?假设我们在两个向量中都位于元素 0 并且 if 条件适用。现在我从两个向量中删除元素 0,然后增加索引,同时我的旧元素 1 现在是元素 0。所以我将从原始向量中跳过元素 1。还是我错了?
  • @AlexG 好吧,我想我可以对它们进行排序,但排序也需要时间,对吧?!那我赢什么?除此之外:性能在这里并不重要,因为向量大约有 1 到 5 个元素
  • ` left_unprocessed_event_arrays.erase(it1); right_unprocessed_event_arrays.erase(it2); ` it1 和 it2 无效,不是吗?所以 it1++ 和 it2++ 是未定义的行为。
  • @MatthieuBrucher 我知道。这就是我在问题中写的,不是吗?

标签: c++ loops c++11 iterator stdvector


【解决方案1】:

使用标准算法可以明确您想要做什么并使控制流程简单明了(不像goto):

for (auto it1 = left_unprocessed.begin(); it1 != left_unprocessed.end(); )
{
  auto eventsMatch = [&event1 = *it1](const auto& event2) {
    return event1.header.stamp.nsec == event2.header.stamp.nsec;
  };

  auto it2 = std::find_if(right_unprocessed.begin(), right_unprocessed.end(), eventsMatch);

  if (it2 != right_unprocessed.end())
  {
    matching_event_arrays.push_back({*it1, *it2});
    it1 = left_unprocessed.erase(it1);
    right_unprocessed.erase(it2);
  }
  else
  {
    ++it1;
  }
}

【讨论】:

    【解决方案2】:

    我会使用goto 像这样退出嵌套循环:

    for(auto it1=left_unprocessed_event_arrays.begin();   i1!=left_unprocessed_event_arrays.end();){
      for(auto it2=right_unprocessed_event_arrays.begin(); it2!=right_unprocessed_event_arrays.end();){
        if(it1->header.stamp.nsec==it2->header.stamp.nsec){
          matching_event_arrays.push_back({*it1,*it2});
          it1 = left_unprocessed_event_arrays.erase(it1);
          it2 = right_unprocessed_event_arrays.erase(it2);
          goto next;
        }
        ++it2;
      }
      ++it1;
    next:;
    }
    

    【讨论】:

    • 我认为这不是使用goto 的好地方。您实际上并没有打破多个嵌套循环,只有一个(因此Cpp Core guidelines 也不允许这样做)。只是在这种情况下它看起来不错并不是使用它的好理由 - 如果您将 if 替换为 goto,它也“看起来不错”,但这是普遍不赞成的。
    • 更具体地说,更改it1 的两个地方相距甚远,这使得验证循环是否真正终止变得更加困难。这使得代码在面对变化时更加脆弱(这也是为什么goto 通常最好避免的主要原因之一)。
    • @MaxLanghof 循环足够短。在这种情况下,goto 不仅“看起来不错”,而且“还不错”。务实。指导方针就是:指导方针。也就是说,在一般情况下,您使用 std 算法的解决方案更可取。赞成。
    【解决方案3】:

    看看这个例子,也许会有所帮助:

    int main()
    {
        vector<int> a = { 1, 2, 3, 7, 2 };
        vector<int> b = { 2, 3, 4, 9 };
    
        auto cmp = [&b](int x) {return std::find(b.begin(), b.end(), x) != b.end();};
    
        while(1)
        {
            auto it = std::find_if(a.begin(), a.end(), cmp);
            if(it == a.end())
                break;
            auto val = *it;
            a.erase(std::remove(a.begin(), a.end(), val), a.end());
            b.erase(std::remove(b.begin(), b.end(), val), b.end());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-24
      • 2021-01-02
      相关资源
      最近更新 更多