【问题标题】:Is there a way to iterate over two containers without using two for loops有没有办法在不使用两个 for 循环的情况下迭代两个容器
【发布时间】:2019-06-17 13:46:16
【问题描述】:

有没有办法在不使用两个 for 循环的情况下迭代两个容器(一个接着另一个)。

我的目的是做这样的事情

vector<int> a{ 1,2,3 };
vector<int> b{ 4,5,6 };

auto it = a.begin();
auto end = b.end();

for (; it != end; ++it)
{
    if (it == a.end())
    {
        it = b.begin();
    }
    // do something with *it
}

打印

1 2 3 4 5 6

(当然不行,解释在这个answer

我不想编写两个 for 循环并在循环内复制代码。 有没有办法用一个 for 循环遍历 a 后跟 b

我唯一能想到的是将第二个容器复制/移动到第一个容器,或者创建一个结合ab 的新向量,然后对其进行迭代。我也不想这样做,因为这将意味着昂贵的复制操作。

【问题讨论】:

  • 如何编写两个循环(或使用标准算法,如std::for_each)并在将传递给所述算法的单个函数中实现重复的行为?
  • @Fureeish 是的,这是可能的
  • 有人愿意解释为什么这个问题被否决了吗?我做了研究,这不是家庭作业问题。
  • 那么我会采用这种方法。强制将逻辑封闭在一个循环中会引入许多不必要的检查和难以阅读的代码噪音。喜欢简单而不是复杂,特别是如果行为性能相同,或者就更简单的方法而言更好。

标签: c++ c++11 c++14 c++17


【解决方案1】:

使用range-v3,C++17 或更早版本中与范围相关的所有内容的首选:

for (int i : view::concat(a, b)) {
    std::cout << i << ' ';
}

【讨论】:

    【解决方案2】:

    另一种使用提升范围的方法

    #include <vector>
    #include <iostream>
    
    #include <boost/range.hpp>
    #include <boost/range/join.hpp>
    
    int main()
    {
      std::vector<int> a{ 1,2,3 };
      std::vector<int> b{ 4,5,6 };
    
      for(auto& x : boost::join(a, b)) {
          std::cout << x << " ";
      }
      std::cout << std::endl;
    }
    

    【讨论】:

      【解决方案3】:

      Boost Range 和标准库算法是应该首选的解决方案,因为它们的设计更好。

      但是,为了完整起见,如果您真的想在您的设计背后应用这个想法,您可以编写如下代码:

      std::vector<int> v1 = {1, 2, 3};
      std::vector<int> v2 = {4, 5, 6};
      
      for (auto it = v1.begin(); it != v2.end();) {
        if (it == v1.end()) {
          it = v2.begin();
        } else {
        // {
          // use of *it
        // }
          ++it;
        }
      }
      

      Live Demo Here

      【讨论】:

      • 使用 Visual Studio 2017,我收到错误“向量迭代器不兼容”。我猜这是因为当 v1 是 v1 迭代器时,我们将它与 v2.end() 进行比较。
      • 如果 v1 和 v2 都为空,则需要进行一些额外的检查。
      • @AndyG 不。该代码检查所有情况。我不认为这里有任何 UB
      • @BiagioFesta:你说得对,抱歉。我将use of *it 误读为else 之外的@
      【解决方案4】:

      你可以像这样使用boost::range::join

      #include <boost/range/join.hpp>
      
      ...
      
      std::vector<int> a{ 1,2,3 };
      std::vector<int> b{ 4,5,6 };
      
      for (auto i : boost::range::join(a, b))
      {
          ...
      }
      

      【讨论】:

        【解决方案5】:

        找到了一种简单的“传统”方式来做到这一点。

        for (int i = 0; i < 2; i++)
        {
            auto it = (i == 0) ? a.begin() : b.begin();
            auto end = (i == 0) ? a.end() : b.end();
            for (; it != end; ++it)
            {
                // do something with *it
            }
        }
        

        【讨论】:

        • 但这使用了两个 for 循环?
        • @AndyG 对不起,如果我把大家弄糊涂了,我的意思是“不使用两个循环,一个接着另一个”。不希望代码“//用 *it 做某事”重复
        【解决方案6】:

        如果您想自己编写,以下内容会有所帮助:

        template<class ForwardItr>
        struct range {
            ForwardItr beg;
            ForwardItr end;
        };
        
        template<class ForwardItr, class F>
        void concat_ranges(range<ForwardItr> r1, range<ForwardItr> r2, F f) {
            auto run = [&f](range<ForwardItr> r) {
                for(auto itr = r.beg; itr != r.end; ++itr){
                    f(*itr);
                }
            };
            run(r1);
            run(r2);
        };
        

        示例:https://gcc.godbolt.org/z/8tPArY

        【讨论】:

          【解决方案7】:

          甚至没有一个for()循环需要打印这些容器,如果你使用std::copy如下,

          #include <iostream>
          
          #include <vector>
          
          #include <iterator>
          #include <algorithm>
          
          int main(int , char *[])
          {
              std::vector< int> a{ 1, 2, 3};
              std::vector< int> b{ 4, 5, 6};
          
              std::copy( a.begin(), a.end(), std::ostream_iterator< int>( std::cout, " "));
              std::copy( b.begin(), b.end(), std::ostream_iterator< int>( std::cout, " "));
          
              std::cout<< std::endl;
          
              return 0;
          }
          

          输出:1 2 3 4 5 6

          使用 stl 库是最好的选择,并且不需要编写任何代码来打印容器。

          根据您的担忧我不想编写两个 for 循环并在循环内复制代码。有没有办法用一个 for 循环遍历 a 后跟 b?

          避免重复代码的方法是编写可以在多个地方使用的函数,例如,如果您不想使用 std::copy 并想编写自己的代码来打印这些容器(不推荐)然后你可以编写以下函数,

          template< typename ForwardIterator>
          void print( ForwardIterator begin, ForwardIterator end, const std::string& separator)
          {
              while( begin != end)
              {
                  std::cout<< *begin<< separator;
                  ++begin;
              }
          }
          

          然后调用print函数,

          print( a.begin(), a.end(), std::string( " "));
          print( b.begin(), b.end(), std::string( " "));
          

          【讨论】:

            【解决方案8】:

            嗯...你的错误是一个双等号,而你需要一个单等号。

            我的意思是,不是

            if (it == a.end())
            {
                it == b.begin();
            } //   ^^ Wrong!
            

            但是

            if (it == a.end())
            {
                it = b.begin();
            } //   ^ correct
            

            但我认为这不是一个好主意:我们确定 a.end() != b.end()

            您的代码依赖于此。

            【讨论】:

              【解决方案9】:

              为了获得最佳代码,最好避免在每个循环中进行不必要的测试。由于最有效的代码在于执行两个循环,因此可以通过更改参与循环的变量(迭代器和哨兵)的整个状态来接近它,(我想这就是如何实现连接范围。 ..我就是这样做的):

              vector<int> a{ 1,2,3 };
              vector<int> b{ 4,5,6 };
              
              auto it = a.begin();
              auto end = a.end();
              
              for (;[&](){
                       if (it==end){
                          if (end==a.end()) {
                            it=b.begin();
                            end=b.end();
                            return true;
                            }
                          else return false;
                          }
                        return true;
                        }();
                  ++it)
              {
                 //loop content
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2021-06-16
                • 1970-01-01
                • 2020-12-20
                • 2017-03-25
                • 2021-12-04
                • 1970-01-01
                • 1970-01-01
                • 2022-12-19
                相关资源
                最近更新 更多