【问题标题】:How to implement Kahn's algorithm for a topological sort in rust?如何在 Rust 中实现 Kahn 的拓扑排序算法?
【发布时间】:2022-11-26 20:07:23
【问题描述】:

我想为有向无环图实现 Kahn 算法。

我理解为什么在 Rust 中不可能多次借用可变变量,而且如果可变变量已经借用为不可变变量,我也不能借用它,但我不想使用引用计数指针,因为它在堆上分配内存。

struct Node {
    ref_count: u32,
    id: usize,
    text: String
}

impl Node {
    fn new(text: String) -> Self {
        Node {
            ref_count: 0,
            id: 0,
            text: text
        }
    }

    fn get_ref_count(&self) -> u32 {
        self.ref_count
    }

    fn get_id(&self) -> usize {
        self.id
    }

    fn execute(&self) {
        println!("{}", self.text);
    }
}

struct Edge {
    from: usize,
    to: usize
}

impl Edge {
    fn new(from: &Node, to: &Node) -> Self {
        Edge {
           from: from.get_id(),
           to: to.get_id() 
        }
    }
}

struct Dag {
    nodes: Vec<Node>,
    edges: Vec<Edge>
}

impl Dag {
    fn new() -> Self {
        Dag {
            nodes: vec![],
            edges: vec![]
        }
    }

    fn add_node(&mut self, node: Node) {
        let id = self.nodes.len();
        self.nodes.push(node);
        self.nodes[id].id = id;
    }

    fn add_edge(&mut self, edge: Edge) {
        self.edges.push(edge);
    }

    fn sort_and_execute(&mut self) {
        for edge in &self.edges {
            let node = &mut self.nodes[edge.to];
            node.ref_count+=1;
        }

        let mut stack: Vec<&Node> = vec![];
        for node in &self.nodes {
            if node.get_ref_count() == 0 {
                stack.push(node);
            }
        }

        while !stack.is_empty() {
            let node = stack.pop();
            if let Some(node) = node {
                node.execute();
                let edges: Vec<&Edge> = self.edges
                    .iter()
                    .filter(|edge| edge.from == node.get_id() )
                    .collect();
                for edge in edges {
                    //linked node must be mutable, because the ref count has to be decremented
                    let linkedNode = &mut self.nodes[edge.to];
                    linkedNode.ref_count -= 1;
                    if linkedNode.get_ref_count() == 0 {
                        stack.push(linkedNode);
                    }
                }
            }
        }
    }
}

fn main() {
    let a = Node::new("0".to_owned());
    let b = Node::new("1".to_owned());
    let c = Node::new("2".to_owned());
    let d = Node::new("3".to_owned());

    let a_c = Edge::new(&a, &c);
    let b_c = Edge::new(&b, &c);
    let c_d = Edge::new(&c, &d);

    let mut dag = Dag::new();
    dag.add_node(a);
    dag.add_node(b);
    dag.add_node(c);
    dag.add_node(d);

    dag.add_edge(a_c);
    dag.add_edge(b_c);
    dag.add_edge(c_d);
    
    dag.sort_and_execute();
}

问题是在73行中,for循环借用self.nodes来查找带有ref_count = 0的节点,在89行中,self.nodes也必须借用(可变地)以减少引用计数

有什么办法可以做到吗?

【问题讨论】:

  • 请注意,Vec 也在堆上分配内存。话虽这么说,你可以使用RefCell,它使用你放置它的任何地方的内存(例如,堆栈用于局部变量,堆用于Box&lt;RefCell&lt;_&gt;&gt;,直接在数组中用于数组或Vec&lt;RefCell&lt;_&gt;&gt;)。
  • 是的,Vec 也在堆上分配了内存,这是正确的,但是要处理这个算法,必须有某种存储。问题是在73行中,for循环借用了self.nodes,在89self.nodes行中,必须可变地借用以减少引用计数
  • 如果将self.nodes更改为Vec&lt;RefCell&lt;Node&gt;&gt;,那么即使在for循环中借用self.nodes,您也可以执行node.borrow_mut().ref_count += 1;
  • 哦,好的,我以为你的意思是 stackRefCell。我的错。我会在一分钟内检查出来。谢谢
  • stack变量可以是Vec&lt;Ref&lt;Node&gt;&gt;,然后像这样推入这个向量:stack.push(node.borrow());吗?这个场景下的Ref&lt;Node&gt;是不是也用stack?

标签: rust


【解决方案1】:

您应该更详细地阅读interior mutability,以便能够惯用地实现这一点。虽然 RefCell 是这个问题的一个可能的解决方案,但如果您根据情况使用 Cell,甚至 AtomicU32,它可以变得更简单并且计算成本可能更低。

【讨论】:

    【解决方案2】:

    您可以通过使用 Vec&lt;usize&gt; (node.get_id()) 而不是 Vec&lt;&amp;Node&gt; 作为变量 stack 来避免这种情况。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-26
      • 2017-09-27
      • 2017-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多