如何检测 std::map 循环中的最后一次迭代?

Posted

技术标签:

【中文标题】如何检测 std::map 循环中的最后一次迭代?【英文标题】:How can I detect the last iteration in a loop over std::map? 【发布时间】:2008-09-29 22:40:59 【问题描述】:

我正在尝试找出最好的方法来确定我是否处于地图上循环的最后一次迭代中,以便执行以下操作:

for (iter = someMap.begin(); iter != someMap.end(); ++iter) 
    bool last_iteration;
    // do something for all iterations
    if (!last_iteration) 
        // do something for all but the last iteration
    

似乎有几种方法可以做到这一点:随机访问迭代器、distance 函数等。规范方法是什么?

编辑:地图没有随机访问迭代器!

【问题讨论】:

【参考方案1】:

规范?我不能这么说,但我建议

final_iter = someMap.end();
--final_iter;
if (iter != final_iter) ...

已编辑,按照 KTC 的建议进行更正。(谢谢!有时你走得太快,把最简单的事情搞砸了……)

【讨论】:

减量必须是预减量,否则 finalIter 将结束。 CAVEAT:它需要双向迭代器,因此它不适用于所有 STL 集合类。 你可以与 rbegin() 的值进行比较 @ffpf,你不能,因为正向迭代器和反向迭代器之间的比较不起作用。 除非地图在循环内发生变化,否则 final_iter 应该在循环外进行评估。在这种情况下,必须检查地图是否为空,以免我们遇到可怕的未定义行为。【参考方案2】:

从C++11开始,也可以使用std::next()

   for (auto iter = someMap.begin(); iter != someMap.end(); ++iter)  
        // do something for all iterations
        if (std::next(iter) != someMap.end()) 
            // do something for all but the last iteration
        
    

虽然这个问题是不久前提出的,但我认为值得分享。

【讨论】:

【参考方案3】:

这似乎是最简单的:

bool last_iteration = iter == (--someMap.end());

【讨论】:

迭代器真的用整数定义算术加法吗?我不这么认为;但即使他们中的一些人这样做,我敢肯定你不能依赖它每个容器。 这行不通... [iterator type] + int 运算符不匹配! 双向迭代器(map 有什么)没有 + 定义来操作它。 是否定义了将迭代器递减为空集合? @Tim:如果它是一个空集合,我们将永远不会进入循环体。 :-)【参考方案4】:

如果您只想使用 ForwardIterator,这应该可以:

for ( i = c.begin(); i != c.end(); ) 
        iterator cur = i++;
        // do something, using cur
        if ( i != c.end() ) 
                // do something using cur for all but the last iteration
        

【讨论】:

【参考方案5】:

修改了 Mark Ransom 的,因此它实际上可以按预期工作。

finalIter = someMap.end();
--finalIter;
if (iter != final_iter)

【讨论】:

【参考方案6】:

很惊讶还没有人提到它,但当然 boost 有一些东西 ;)

Boost.Next(和等效的 Boost.Prior)

您的示例如下所示:

for (iter = someMap.begin(); iter != someMap.end(); ++iter) 
    // do something for all iterations
    if (boost::next(iter) != someMap.end()) 
        // do something for all but the last iteration
    

【讨论】:

我很确定它现在在标准库中,如std::next 等。【参考方案7】:

以下代码将由编译器进行优化,以便在性能和 OOP 规则方面成为该任务的最佳解决方案:

if (&*it == &*someMap.rbegin()) 
    //the last iteration

这是 OOP 规则中最好的代码,因为 std::map 有一个特殊的成员函数 rbegin 用于如下代码:

final_iter = someMap.end();
--final_iter;

【讨论】:

【参考方案8】:

为什么要努力找到EOF,这样你就不会给它一些东西。

简单地说,排除它;

for (iter = someMap.begin(); someMap.end() - 1; ++iter) 
    //apply to all from begin to second last element

亲吻(保持简单)

【讨论】:

你确定这行得通吗?迭代器是否用ints 定义operator-【参考方案9】:
#include <boost/lambda/lambda.hpp>
#include <boost/lambda/bind.hpp>
#include <algorithm>

using namespace boost::lambda;

// call the function foo on each element but the last...
if( !someMap.empty() )

  std::for_each( someMap.begin(), --someMap.end(), bind( &Foo, _1 ) );

使用 std::for_each 将确保循环紧密且准确...注意函数 foo() 的引入,该函数采用单个参数(类型应与 someMap 中包含的内容匹配)。这种方法增加了 1 行。当然,如果 Foo 真的很小,你可以使用 lambda 函数并摆脱对 &Foo 的调用。

【讨论】:

这不允许“为所有迭代做某事”【参考方案10】:

这个怎么样,没有人提到,但是...

for (iter = someMap.begin(); iter != someMap.end(); ++iter) 
    // do something for all iterations
    if (iter != --someMap.end()) 
        // do something for all but the last iteration
    

这看起来很简单,嗯...

【讨论】:

【参考方案11】:

对于喜欢 C++11 基于范围的循环的人:

    for (const auto& pair : someMap) 
      if (&pair != &*someMap.rbegin()) ...
    

注意这里只有引用类型有效,auto pair 无效

【讨论】:

这个答案让我有些兴趣,因为我也喜欢 C++11 循环。但它似乎要求在地址意义上的“&”的结果在获取变量pair 的地址和获取*someMap.rbegin() 的结果地址之间保持一致。 const auto&amp; pair 的值是基于 begin()..end() 而不是 rend()..rbegin() 所以似乎有人怀疑这在编译器和库中是否有效。【参考方案12】:

一种简单而有效的方法:

  size_t items_remaining = someMap.size();

  for (iter = someMap.begin(); iter != someMap.end(); iter++) 
    bool last_iteration = items_remaining-- == 1;
  

【讨论】:

【参考方案13】:

完整程序:

#include <iostream>
#include <list>

void process(int ii)

   std::cout << " " << ii;


int main(void)

   std::list<int> ll;

   ll.push_back(1);
   ll.push_back(2);
   ll.push_back(3);
   ll.push_back(4);
   ll.push_back(5);
   ll.push_back(6);

   std::list<int>::iterator iter = ll.begin();
   if (iter != ll.end())
   
      std::list<int>::iterator lastIter = iter;
      ++ iter;
      while (iter != ll.end())
      
         process(*lastIter);
         lastIter = iter;
         ++ iter;
      
      // todo: think if you need to process *lastIter
      std::cout << " | last:";
      process(*lastIter);
   

   std::cout << std::endl;

   return 0;

这个程序产生:

 1 2 3 4 5 | last: 6

【讨论】:

【参考方案14】:

这是我的优化:

iter = someMap.begin();

do 
    // Note that curr = iter++ may involve up to three copy operations
    curr = iter;

    // Do stuff with curr

    if (++iter == someMap.end()) 
        // Oh, this was the last iteration
        break;
    

    // Do more stuff with curr

 while (true);

【讨论】:

【参考方案15】:

您可以在迭代之前从地图中拉出一个元素,然后在循环之外执行“最后一次迭代”工作,然后将元素放回地图。这对于异步代码来说是非常糟糕的,但是考虑到 C++ 的其余部分对于并发性有多么糟糕,我认为这不会是一个问题。 :-)

【讨论】:

地图是有序的 - 也许你正在考虑哈希? 我的印象是地图是默认使用哈希表实现的。我会更正答案。谢谢!

以上是关于如何检测 std::map 循环中的最后一次迭代?的主要内容,如果未能解决你的问题,请参考以下文章

如何遍历地图,修改地图但在每次迭代时恢复?

如何获取 BTreeMap 中的最后一项?

如何在 API 中隐藏迭代器以使用 std::map 包装器中的项目

我可以使用基于范围的 for 循环轻松迭代地图的值吗?

For循环中的Python最后一次迭代

检测python中dictionary.iteritems()的最后一次迭代