代码之家  ›  专栏  ›  技术社区  ›  mizkichan

有没有一种方法可以迭代可变树以获得随机节点?

  •  3
  • mizkichan  · 技术社区  · 7 年前

    我正在尝试更新树结构的节点。随机选择要更新的节点。要使用水库采样算法对树中的节点进行采样,我必须迭代节点,因此我尝试 Iterator 对于我的 Node 枚举。

    问题是,一方面,我必须在堆栈或队列中存储子节点的引用,但另一方面,我必须返回父节点的可变引用。Rust不允许为一个值创建多个可变引用,也不允许将不可变引用转换为可变引用。

    有没有一种方法可以迭代可变树?或者有没有其他方法可以随机获取树中节点的可变引用?

    这是我的密码。

    #![feature(box_syntax, box_patterns)]
    extern crate rand;
    
    // Simple binary tree structure
    #[derive(Debug)]
    enum Node {
        Leaf(u8),
        Branch(Box<Node>, Box<Node>),
    }
    
    impl Node {
        fn iter_mut(&mut self) -> IterMut {
            IterMut {
                stack: vec![self],
            }
        }
    
        fn pick_random_node_mut<'a>(&'a mut self) -> &'a mut Node {
            // Revervoir sampling
            let rng = &mut rand::thread_rng();
            rand::seq::sample_iter(rng, self.iter_mut(), 1)
                .ok().and_then(|mut v| v.pop()).unwrap()
        }
    }
    
    // An iterator for `Node`
    struct IterMut<'a> {
        stack: Vec<&'a mut Node>,
    }
    
    impl <'a> Iterator for IterMut<'a> {
        type Item = &'a mut Node;
    
        fn next(&mut self) -> Option<&'a mut Node> {
            let node = self.stack.pop()?;
    
            // I am stucking here: cannot borrow `*node` as mutable more than once at a time
            if let &mut Node::Branch(box ref mut a, box ref mut b) = node {
                self.stack.push(b);
                self.stack.push(a);
            }
            Some(node)
        }
    }
    
    fn main() {
        use Node::*;
    
        let mut tree: Node = Branch(box Leaf(1), box Leaf(2));
        println!("{:?}", tree);
    
        {
            let node: &mut Node = tree.pick_random_node_mut();
            *node = Leaf(3);
        }
        println!("{:?}", tree);
    
    }
    
    1 回复  |  直到 7 年前
        1
  •  3
  •   Shepmaster Tim Diekmann    6 年前

    不,为树的节点编写可变引用的迭代器是不安全的。

    假设我们有这样的树结构:

             +-+
        +----+ +----+
        |    +-+    |
        |           |
        |           |
     +--v-+      +--v--+
     | 50 |      | 100 |
     +----+      +-----+
    

    如果存在这样的迭代器,我们可以这样调用它:

    let mut all_nodes: Vec<&mut Node> = tree.iter_mut().collect();
    

    假设父节点在索引0中结束,左节点在索引1中结束,右节点在索引2中结束。

    let (head, tail) = all_nodes.split_at_mut(1);
    
    let x = match &mut head[0] {
        Branch(ref mut l, _) => l,
        Leaf(_) => unreachable!(),
    };
    
    let y = &mut tail[1];
    

    现在 x y 是彼此可变的别名。我们有 违反了完全安全规范中的基本防锈要求 . 这就是为什么这样的迭代器是不可能的。


    您可以实现对 价值观 在树中:

    impl<'a> Iterator for IterMut<'a> {
        type Item = &'a mut u8;
    
        fn next(&mut self) -> Option<Self::Item> {
            loop {
                let node = self.stack.pop()?;
    
                match node {
                    Node::Branch(a, b) => {
                        self.stack.push(b);
                        self.stack.push(a);
                    }
                    Node::Leaf(l) => return Some(l),
                }
            }
        }
    }
    

    这是安全的,因为无法从一个可变引用到另一个值。然后,您可以在此基础上构建随机选择:

    {
        let rando = match rand::seq::sample_iter(&mut rand::thread_rng(), tree.iter_mut(), 1) {
            Ok(mut v) => v.pop().unwrap(),
            Err(_) => panic!("Not enough elements"),
        };
    
        *rando += 1;
    }