双重循环遍历 Rust 中的一个集合,其中一个循环会对其进行更改。

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

Double loop over one collection in Rust where one loop changes it

问题

我试图迭代集合`ants`(`Vec<Ant>`)。我不能克隆对象。

```lang-rust
for ant1 in &mut self.ants {
  for ant2 in &self.ants {
    if ant1 != ant2 && distance(ant1, ant2) <= Ant::visibility_range() {
      ant1.meet(ant2); // fn meet(&mut self, other: &Self); // - 需要。
    }
  }
}

借用检查器出现问题。如何正确做?

一个解决方案是复制所需的字段吗?


<details>
<summary>英文:</summary>

I&#39;m trying to iterate over the collection `ants` (`Vec&lt;Ant&gt;`). I can&#39;t clone the objects.

```lang-rust
for ant1 in &amp;mut self.ants {
  for ant2 in &amp;self.ants {
    if ant1 != ant2 &amp;&amp; distance(ant1, ant2) &lt;= Ant::visibility_range() {
      ant1.meet(ant2); // fn meet(&amp;mut self, other: &amp;Self); // - needs.
    }
  }
}

The borrow checker hits my hands. How to do that rightly?

Would a solution be to copy needed fields?

答案1

得分: 3

你可以使用 split_at_mut 来仅查看稍后的蚂蚁。

for outer in 1..self.ants.len() {
    let (begin, end) = self.ants.split_at_mut(outer);
    let ant1 = begin.last_mut().unwrap();
    for ant2 in end {
        if ant1 != ant2 && distance(ant1, ant2) <= Ant::visibility_range() {
            ant1.meet(ant2);
            ant2.meet(ant1);
        }
    }
}

使用这种实现方式,一只蚂蚁无法与自己相遇,但无论如何,由于 meet 的签名,这是不可能的。这只有在一只蚂蚁可以与自身不相等时才重要,所以如果你有一个合理的 PartialEq 实现,这不会有关。

这个实现还改变了相遇的顺序。ant1 将在相同位置与 ant2 相遇,但与你的代码相比,ant2 会比 ant1 更早地与 ant1 相遇。它的优点是计算 distance 的次数减少了一半。

如果你想保持顺序,可以遍历之前和之后的蚂蚁。

for outer in 1..self.ants.len() {
    let (begin, end) = self.ants.split_at_mut(outer);
    let (ant1, begin) = begin.split_last_mut().unwrap();
    for ant2 in begin.into_iter().chain(end) {
        if ant1 != ant2 && distance(ant1, ant2) <= Ant::visibility_range() {
            ant1.meet(ant2);
        }
    }
}

再次强调,这不会比较一只蚂蚁与自己,但除此之外,与你的代码相同。

英文:

You can use split_at_mut to only look at later ants.

for outer in 1..self.ants.len() {
    let (begin, end) = self.ants.split_at_mut(outer);
    let ant1 = begin.last_mut().unwrap();
    for ant2 in end {
        if ant1 != ant2 &amp;&amp; distance(ant1, ant2) &lt;= Ant::visibility_range() {
            ant1.meet(ant2);
            ant2.meet(ant1);
        }
    }
}

With this implementation, an ant can't meet itself, but that is impossible anyway due to the signature of meet. That is only important if an ant can be inequal to itself, so if you have a sensible PartialEq implementation, this doesn't matter.

This implementation also changes the order of meets. ant1 will meet ant2 at the same point, but ant2 will meet ant1 earlier than in your code. It has the advantage of calculating distance half as many times, though.

If you want to keep the order, you can loop over previous and later ants.

for outer in 1..self.ants.len() {
    let (begin, end) = self.ants.split_at_mut(outer);
    let (ant1, begin) = begin.split_last_mut().unwrap();
    for ant2 in begin.into_iter().chain(end) {
        if ant1 != ant2 &amp;&amp; distance(ant1, ant2) &lt;= Ant::visibility_range() {
            ant1.meet(ant2);
        }
    }
}

Again, this doesn't compare an ant to itself, but everything else is the same as your code.

huangapple
  • 本文由 发表于 2023年7月3日 02:08:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76600202.html
匿名

发表评论

匿名网友

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

确定