简体   繁体   English

在 Rust 的单链表中实现 .pop() 的更好方法是什么?

[英]What would be a better way to implement .pop() in my single linked list in Rust?

I've implemented my own version of a singly linked list in Rust as one of the challenges for me to learn it, and I'm satisfied with everything I have there except for the .pop() method.我已经在 Rust 中实现了我自己的单链表版本,这是我学习它的挑战之一,除了 .pop() 方法之外,我对那里的所有东西都很满意。 Using 2 while loops is very ugly and inefficient, but I found no other way to overcome the problem of setting the node at the index len() - 2 to None (popping the list), and using the data from the node at the index len() - 1 for the Some(data) return value (returns the element that was popped).使用 2 while 循环非常丑陋且效率低下,但我发现没有其他方法可以克服将索引处的节点 len() - 2 设置为 None(弹出列表),并使用来自索引处的节点的数据的问题len() - 1 表示 Some(data) 返回值(返回被弹出的元素)。

GitHub Link GitHub 链接

pub struct SimpleLinkedList<T> {
    head: Option<Box<Node<T>>>,
}

struct Node<T> {
    data: T,
    next: Option<Box<Node<T>>>,
}

impl<T> Default for SimpleLinkedList<T> {
    fn default() -> Self {
        SimpleLinkedList { head: None }
    }
}

impl<T: Copy> Clone for SimpleLinkedList<T> {
    fn clone(&self) -> SimpleLinkedList<T> {
        let mut out: SimpleLinkedList<T> = SimpleLinkedList::new();
        let mut cur = &self.head;
        while let Some(node) = cur {
            cur = &node.next;
            out.push(node.data)
        }
        out
    }
}

impl<T> SimpleLinkedList<T> {
    pub fn new() -> Self {
        Default::default()
    }

    pub fn len(&self) -> usize {
        let mut c = 0;
        let mut cur = &self.head;
        while let Some(node) = cur {
            cur = &node.next;
            c += 1;
        }
        c
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    pub fn push(&mut self, _element: T) {
        let mut cur = &mut self.head;
        match cur {
            Some(_) => {
                while let Some(node) = cur {
                    cur = &mut node.next;
                }
            }
            None => (),
        }
        *cur = Some(Box::from(Node {
            data: _element,
            next: None,
        }));
    }

    pub fn pop(&mut self) -> Option<T>
    where
        T: Copy,
    {
        let length = &self.len();
        let mut cur = &mut self.head;
        let mut out = None;
        match cur {
            Some(_) if *length > 1usize => {
                let mut c = 0usize;
                while let Some(node) = cur {
                    cur = &mut node.next;
                    if c >= length - 1 {
                        out = Some(node.data);
                        break;
                    }
                    c += 1;
                }

                c = 0usize;
                cur = &mut self.head;
                while let Some(node) = cur {
                    cur = &mut node.next;
                    if c == length - 2 {
                        break;
                    }
                    c += 1;
                }
            }
            Some(node) => out = Some(node.data),
            None => (),
        }
        *cur = None;
        out
    }

    pub fn peek(&self) -> Option<&T> {
        let cur = &self.head;
        match cur {
            Some(node) => Some(&node.data),
            None => None,
        }
    }
}

impl<T: Copy> SimpleLinkedList<T> {
    pub fn rev(&self) -> SimpleLinkedList<T> {
        let mut clone = self.clone();
        let mut out: SimpleLinkedList<T> = SimpleLinkedList::new();
        while let Some(val) = clone.pop() {
            out.push(val)
        }
        out
    }
}

impl<'a, T: Copy> From<&'a [T]> for SimpleLinkedList<T> {
    fn from(_item: &[T]) -> Self {
        let mut out: SimpleLinkedList<T> = SimpleLinkedList::new();
        for &e in _item.iter() {
            out.push(e);
        }
        out
    }
}

impl<T> Into<Vec<T>> for SimpleLinkedList<T> {
    fn into(self) -> Vec<T> {
        let mut out: Vec<T> = Vec::new();
        let mut cur = self.head;
        while let Some(node) = cur {
            cur = node.next;
            out.push(node.data)
        }
        out
    }
}

You can avoid re-traversing the list by keeping track of the last element you saw as you go (and then updating that at the end).你可以通过跟踪你看到的最后一个元素来避免重新遍历列表(然后在最后更新它)。

If you are naive about how you do that, you will run into trouble;如果你对如何做到这一点很天真,你就会遇到麻烦; your "previous" pointer retains ownership of the rest of the list and the borrow checker won't allow that.您的“前一个”指针保留列表其余部分的所有权,借用检查器不允许这样做。 The trick is to break that link as you go - and to do that you can use the mem::replace function.诀窍是在您进行时断开该链接 - 为此您可以使用mem::replace函数。 Once you've done that, you have to put it back before you lose track of your previous node again.完成此操作后,您必须将其放回原处,以免再次失去对先前节点的跟踪。

Here's what that could look like in full (you'll have to forgive my liberal use of unwrap - I do think it makes things clearer):以下是完整的内容(您必须原谅我对unwrap自由使用 - 我确实认为它使事情更清晰):

pub fn pop(&mut self) -> Option<T>
    where T : Copy,
{
    use std::mem::replace;

    let curr = replace(&mut self.head, None);

    if curr.is_none() { // list started off empty; nothing to pop
        return None;
    }

    let mut curr = curr.unwrap(); // safe because of the check above

    if let None = curr.next { // popped the last element
        return Some(curr.data);
    }

    let mut prev_next = &mut self.head;

    while curr.next.is_some() {
        // Take ownership of the next element
        let nnext = replace(&mut curr.next, None).unwrap();

        // Update the previous element's "next" field
        *prev_next = Some(curr);

        // Progress to the next element
        curr = nnext;

        // Progress our pointer to the previous element's "next" field
        prev_next = &mut prev_next.as_mut().unwrap().next;

    }

    return Some(curr.data);
}

As an aside, all this pointer shuffling simplifies a lot if you're willing to change the interface a little so that we return a "new" list each time (taking ownership in the pop function), or use a persistent datastructure, as they do in Learning Rust with entirely too many linked lists (already mentioned in a comment):顺便说一句,如果您愿意稍微更改接口以便我们每次都返回一个“新”列表(在pop函数中获得所有权),或者使用持久数据结构,那么所有这些指针混洗都会简化很多,因为它们在学习 Rust 中使用太多的链表(已经在评论中提到):

pub fn pop_replace(self) -> (Option<T>, Self) {
    // freely mutate self and all the nodes
}

Which you would use like:你会使用像:

let elem, list = list.pop();

inspired from here 灵感来自这里

fn pop(&mut self) -> Option<T> {
        let mut current: &mut Option<Box<Node<T>>> = &mut self.head;
        loop {
            // println!("curr: {:?}", current);
            match current {
                None => {
                    return None;
                }
                Some(node) if node.next.is_none() => {
                    let val = node.data;
                    *current = node.next.take();
                    return Some(val);
                }
                Some(ref mut node) => {
                    current = &mut node.next;
                }
            }
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM