gpt4 book ai didi

loops - 如何改变我正在循环的结构?

转载 作者:行者123 更新时间:2023-11-29 08:01:21 26 4
gpt4 key购买 nike

这个问题的动机是 this CodinGame puzzle .

我正在使用 Dijkstra 方法实现一个基本的寻路算法。它使用 boundary HashMap 和一个 finished HashMap 用于保存与寻路相关的节点信息。在一个特定的循环中,我在 boundary 中找到了最高值的节点,删除节点,将节点添加到finished ,并在 boundary 中添加/更新节点的邻居信息.

试图变异 boundary虽然在它上面循环让 Rust 的借用检查器感到不安,但循环的逻辑对我来说似乎是合理的。我如何重写它以便编译器分享我的信心? (或者修复我遗漏的错误,如果这是问题所在。)

代码:

On Rust Playground here

use std::io;
use std::collections::{HashSet, HashMap};
use std::cmp::Ordering;
use std::cell::RefCell;

struct NodeInfo {
nbrs: HashSet<i32>,
gwlinks: i32,
}

#[derive(PartialEq,PartialOrd)]
struct PFInfo {
avg: f32,
cum: i32,
dist: i32,
prev: Option<i32>,
}

impl Eq for PFInfo {}

impl Ord for PFInfo {
fn cmp(&self, other: &PFInfo) -> Ordering {
match self.partial_cmp(other) {
Some(ord) => ord,
None => Ordering::Equal
}
}
}

type Graph = HashMap<i32, RefCell<NodeInfo>>;
type PFGraph = HashMap<i32, PFInfo>;

// Find the path that passes the most gateway links per distance traveled,
// starting at a given node. This is meant to simulate the behavior of an
// "agent" which traverses the graph in the puzzle mentioned above.
fn generate_path(si: &i32, graph: &Graph) -> Vec<i32> {
let n = graph.len();
let mut boundary = PFGraph::with_capacity(n);
let mut finished = PFGraph::with_capacity(n);

boundary.insert( si.clone(),
PFInfo {
avg: 0.,
cum: graph.get(&si).unwrap().borrow().gwlinks,
dist: 0,
prev: None } );

// Keep grabbing the key corresponding the highest value until `boundary` is
// empty
while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1) {

// Move the node from `boundary` to `finished`
let val = boundary.remove(&currid).unwrap();
finished.insert(currid.clone(), val);

// Add or update all adjacent nodes that are not in `finished`
for nbrid in graph.get(&currid).unwrap()
.borrow()
.nbrs.iter()
.filter(|x| !finished.contains_key(x)) {
let currval = finished.get(&currid).unwrap();
let prev = Some(currid.clone());
let dist = currval.dist + 1;
let cum = currval.cum + graph.get(nbrid).unwrap().borrow().gwlinks;
let avg = cum as f32 / dist as f32;
boundary.insert(
nbrid.clone(),
PFInfo {
avg: avg,
cum: cum,
dist: dist,
prev: prev,
}
);
}
}

let mut path = Vec::new();
let mut currid = finished.iter().max_by_key(|x| x.1).unwrap().0.clone();
path.push(currid.clone());
while let Some(previd) = finished.get(&currid).unwrap().prev {
path.push(previd.clone());
currid = previd.clone();
}
path.reverse();

path
}



macro_rules! parse_input {
($x:expr, $t:ident) => ($x.trim().parse::<$t>().unwrap())
}

#[test]
fn test_generate_path() {
let mut inputs = "8 13 2
6 2
7 3
6 3
5 3
3 4
7 1
2 0
0 1
0 3
1 3
2 3
7 4
6 5
4
5".lines();

let header = inputs.next().unwrap().split_whitespace().collect::<Vec<_>>();
let n = parse_input!(header[0], i32); // the total number of nodes in the level, including the gateways
let l = parse_input!(header[1], i32); // the number of links
let e = parse_input!(header[2], i32); // the number of exit gateways

let mut graph = Graph::with_capacity(n as usize);
for node in 0..n {
graph.insert(node, RefCell::new(NodeInfo{ nbrs: HashSet::new(), gwlinks: 0 }));
}
let graph = graph;

for _ in 0..l as usize {
let link = inputs.next().unwrap();
let nodes = link.split(" ").collect::<Vec<_>>();
let n1 = parse_input!(nodes[0], i32); // N1 and N2 defines a link between these nodes
let n2 = parse_input!(nodes[1], i32);

graph.get(&n1).unwrap().borrow_mut().nbrs.insert(n2);
graph.get(&n2).unwrap().borrow_mut().nbrs.insert(n1);
}

let mut gateways = HashSet::new();
for _ in 0..e as usize {
let ei = parse_input!(inputs.next().unwrap(), i32); // the index of a gateway node
gateways.insert(ei);
}
let gateways = gateways;

for gwid in &gateways {
for gwnbr in &graph.get(gwid).unwrap().borrow().nbrs {
(&graph).get(&gwnbr).unwrap().borrow_mut().gwlinks += 1;
}
}

assert_eq!(generate_path(&0, &graph), vec![0, 3]);
}

错误:

rustc 1.18.0 (03fc9d622 2017-06-06)
error[E0502]: cannot borrow `boundary` as mutable because it is also borrowed as immutable
--> <anon>:53:19
|
50 | while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1) {
| -------- immutable borrow occurs here
...
53 | let val = boundary.remove(&currid).unwrap();
| ^^^^^^^^ mutable borrow occurs here
...
76 | }
| - immutable borrow ends here

error[E0502]: cannot borrow `boundary` as mutable because it is also borrowed as immutable
--> <anon>:66:13
|
50 | while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1) {
| -------- immutable borrow occurs here
...
66 | boundary.insert(
| ^^^^^^^^ mutable borrow occurs here
...
76 | }
| - immutable borrow ends here

error: aborting due to 2 previous errors

最佳答案

我找到了我的问题的解决方案,而且它具有一定的通用性,这正是我所希望的。问题在于,在 while let 语句中创建的隐式引用一直存在到循环末尾,即使它仅在那一行上需要。借位从 .iter() 开始,一旦引用的值在表达式末尾被 cloned 就不再需要了。

while let Some( (currid, _) ) = boundary.iter().max_by_key(|x| x.1).clone() {
// ^---where borrow begins ^---where borrow could end

// Move the node from `boundary` to `finished`
let val = boundary.remove(&currid).unwrap();
finished.insert(currid.clone(), val);

...

} // <--- where borrow does end

诀窍是将 currid 的绑定(bind)移动到循环中。当在 while let 语句中借用值时,借用检查器显然认为借用需要持续整个循环。相反,如果隐式借用是在常规 let 绑定(bind)中进行的,则借用检查器足够智能,可以在行尾安全地丢弃借用。

while !boundary.is_empty() {
let currid = boundary.iter().max_by_key(|x| x.1).unwrap().0.clone();
// ^---where borrow begins ^---where borrow ends
// Move the node from `boundary` to `finished`
let val = boundary.remove(&currid).unwrap();
finished.insert(currid.clone(), val);

...

}

我想这里的要点是,如果您需要在依赖于它的循环中改变结构,请将结构的任何借用放在循环内并使这些借用尽可能短——例如,通过使用 克隆

这可能是提议的 non-lexical lifetimes 最终缓解的情况之一。 .

关于loops - 如何改变我正在循环的结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44835712/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com