如何使用自动化循环替代嵌套的for循环?

huangapple go评论85阅读模式
英文:

How to have a auto for loops, alternative to nested for loops?

问题

std::vector<int> vec{1, 2, 3, 4};

for (auto it = vec.begin(); it != vec.end(); ++it)
{
    for (auto jt = it + 1; jt != vec.end(); ++jt) 
    {
        // 在这里进行比较。
    }
}
英文:

I am wondering if there is something available in C++ which helps us to iterate over the two nested loops while using auto. Say like, I would want to compare an array element with all other elements forward. This is how we do it traditionally:

std::vector&lt;int&gt; vec {1, 2, 3, 4};

for (int i = 0; i &lt; vec.size(); ++i) 
{
    for (int j = i + 1; j &lt; vec.size(); ++j) 
    {
        if (vec[i] == vec[j]) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4
    }
}

The intention is to use auto in order to achieve this.

std::vector&lt;int&gt; vec{1, 2, 3, 4};

for (&lt;auto&gt;&amp; i : vec)
//   ^^^^^^^
{
    // What should I write here so that I compare only the forward elements?
}

We can probably use something like this:

for (auto it = vec.begin(); it != vec.end(); ++it)
{
    for (auto jt = it + 1; jt != vec.end(); ++jt) 
    {
        // Do a comparison here.
    }
}

And the third snapshot again writes more code. I am looking to get more insights on the plain second snapshot.

Mentioned in the question itself.

答案1

得分: 4

I am wondering if there is something available in C++ which helps us to iterate over the two nested loops while using auto.

Not exactly what you wished for; However, with std::ranges::iota_view (Since [tag:C++20]), you could write nested range-based for loops like follows:

#include <ranges> // std::ranges::iota_view

for (const auto i : std::views::iota(0u, std::size(vec)))
{
    for (const auto j : std::views::iota(i + 1u, std::size(vec)))
    {
        if (vec[i] == vec[j])
        {
            // .....
        }
    }
}

See a live demo in godbolt.org

Additionally, this enables you to have i and j be const over the loop scope.

Side note: For compilers older than C++20, one could easily implement an iterator that act exactly like std::ranges::iota_view.

英文:

> I am wondering if there is something available in C++ which helps us to iterate over the two nested loops while using auto.

Not exactly what you wished for; However, with std::ranges::iota_view (Since [tag:C++20]), you could write nested range-based for loops like follows:

#include &lt;ranges&gt; // std::ranges::iota_view

for (const auto i : std::views::iota(0u, std::size(vec)))
{
    for (const auto j : std::views::iota(i + 1u, std::size(vec)))
    {
        if (vec[i] == vec[j])
        {
            // .....
        }
    }
}

See a live demo in godbolt.org

Additionally, this enables you to have i and j be const over the loop scope.


Side note: For compilers older than C++20, one could easily implement an iterator that act exactly like std::ranges::iota_view.

答案2

得分: 0

你可以在循环中使用 std::any_of。类似这样:

#include <algorithm>

...

std::vector<int> vec = {1, 2, 3, 4, 2};
for(auto it = vec.begin(); it != vec.end(); ++it) {
    cout << std::any_of(it + 1, vec.end(), [it](int x){ return x == *it;});
}

这会产生输出结果 01000

英文:

you can use std::any_of in your loop. Something like this:

#include &lt;algorithm&gt;

...

std::vector&lt;int&gt; vec = {1, 2, 3, 4, 2};
for(auto it = vec.begin(); it != vec.end(); ++it) {
    cout &lt;&lt; std::any_of(it + 1, vec.end(), [it](int x){ return x == *it;});
}

this produces an output 01000

答案3

得分: 0

#include <span>;

/*...*/ 

std::size_t offset = 0;

for (auto &i: vec) 
{
    for (auto &j: std::span(vec).subspan(++offset)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

`offset` is guaranteed to be always `<= vec.size()`, hence `subspan()` is [well defined][1].

If you don't want to introduce a new variable, this would also work, but might produce more verbose assembly and look quite unusual:

for (auto &i: vec) 
{
    for (auto &j: std::span(vec).subspan(&i - vec.data() + 1)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

For the shortest assembly, having the outer loop iterate over a span already is the best, so that the span doesn't have to be constructed at each outer iteration.

std::span span(vec);
std::size_t offset = 0;

for (auto &i: span) 
{
    for (auto &j: span.subspan(++offset)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

[1]: https://en.cppreference.com/w/cpp/container/span/subspan
英文:

In c++20, something like this, perhaps?

#include &lt;span&gt;

/*...*/ 

std::size_t offset = 0;

for (auto &amp;i: vec) 
{
    for (auto &amp;j: std::span(vec).subspan(++offset)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

offset is guaranteed to be always &lt;= vec.size(), hence subspan() is well defined.

If you don't want to introduce a new variable, this would also work, but might produce more verbose assembly and look quite unusual:

for (auto &amp;i: vec) 
{
    for (auto &amp;j: std::span(vec).subspan(&amp;i - vec.data() + 1)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

For the shortest assembly, having the outer loop iterate over a span already is the best, so that the span doesn't have to be constructed at each outer iteration.

std::span span(vec);
std::size_t offset = 0;

for (auto &amp;i: span) 
{
    for (auto &amp;j: span.subspan(++offset)) 
    {
        if (i == j) {}
        // Compares 1 with 2, 3 and 4
        // Compares 2 with 3 and 4           
    }
}

答案4

得分: 0

为什么不创建一个SuperIndex类,将这两个循环索引嵌入其中?

#include <iostream>
#include <vector>

class SuperIndex {
public :
  SuperIndex (int s = 0) : siz_ (s), i_ (0), j_ (0) {}
  ~SuperIndex () {}
  SuperIndex (const SuperIndex& si) : siz_ (si.siz_), i_ (si.i_), j_ (si.j_) {}
  SuperIndex& operator = (const SuperIndex& si) {
    siz_ = si.siz_; i_ = si.i_; j_ = si.j_;
    return *this;
  }
  bool operator == (const SuperIndex& si) const {
    return (siz_ == si.siz_) && (i_ == si.i_) && (j_ == si.j_);
  }
  bool operator != (const SuperIndex& si) const {return !operator == (si);}
  int i () const {return i_;}
  int j () const {return j_;}
  void operator ++ () {
    ++j_;
    if (j_ == siz_) {++i_; j_ = i_;}
  }
  bool is_sup () const {return (i_ >= siz_);}
  void write (std::ostream& os) const {
    os << siz_ << ":" << i_ << ", " << j_;
  }
private :
  int siz_;
  int i_;
  int j_;
};

使用示例,只有一个循环:

int main (int argc, char* argv []) {
  std::vector<int> vec {1, 2, 3, 4};
  auto si (SuperIndex (vec.size ()));
  for (;!si.is_sup (); ++si) {
    if (si.i () == si.j ()) continue;
    std::cout << "comparing " << vec [si.i ()] << " and " << vec [si.j ()] << std::endl;
  }
  return 0;
}

基于相同的想法,SuperIndex类可以升级为一个SuperIterator类(成员i_和j_为vector::iterator)...

英文:

Why not create a SuperIndex class embedding the two loop index ?

#include &lt;iostream&gt;
#include &lt;vector&gt;

class SuperIndex {
public :
  SuperIndex (int s = 0) : siz_ (s), i_ (0), j_ (0) {}
  ~SuperIndex () {}
  SuperIndex (const SuperIndex&amp; si) : siz_ (si.siz_), i_ (si.i_), j_ (si.j_) {}
  SuperIndex&amp; operator = (const SuperIndex&amp; si) {
    siz_ = si.siz_; i_ = si.i_; j_ = si.j_;
    return *this;
  }
  bool operator == (const SuperIndex&amp; si) const {
    return (siz_ == si.siz_) &amp;&amp; (i_ == si.i_) &amp;&amp; (j_ == si.j_);
  }
  bool operator != (const SuperIndex&amp; si) const {return !operator == (si);}
  int i () const {return i_;}
  int j () const {return j_;}
  void operator ++ () {
    ++j_;
    if (j_ == siz_) {++i_; j_ = i_;}
  }
  bool is_sup () const {return (i_ &gt;= siz_);}
  void write (std::ostream&amp; os) const {
    os &lt;&lt; siz_ &lt;&lt; &quot;:&quot; &lt;&lt; i_ &lt;&lt; &quot;, &quot; &lt;&lt; j_;
  }
private :
  int siz_;
  int i_;
  int j_;
};

usage, only one loop :

int main (int argc, char* argv []) {
  std::vector&lt;int&gt; vec {1, 2, 3, 4};
  auto si (SuperIndex (vec.size ()));
  for (;!si.is_sup (); ++si) {
    if (si.i () == si.j ()) continue;
    std::cout &lt;&lt; &quot;comparing &quot; &lt;&lt; vec [si.i ()] &lt;&lt; &quot; and &quot; &lt;&lt; vec [si.j ()] &lt;&lt; std::endl;
  }
  return 0;
}

SuperIndex class may be upgraded to a SuperIterator class, based on the same idea (members i_ and j_ are vector::iterator)...

huangapple
  • 本文由 发表于 2023年7月13日 12:22:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/76675911.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定