0

I want to build a tree using exactly two structs: Node and Tree and then recursively search for a target node from the tree. If a target is found, return true, else return false.

The challenge for me here is how to recursively call the find function, since it is only defined on Tree not Node.

pub struct Node<T> {
    value: T,
    left: Option<Box<Node<T>>>,
    right: Option<Box<Node<T>>>,
}

pub struct Tree<T> {
    root: Option<Box<Node<T>>>,
}

impl<T: Ord> Tree<T> {
    /// Creates an empty tree
    pub fn new() -> Self {
        Tree { root: None }
    }

    // search the tree
    pub fn find(&self, key: &T) -> bool {
        let root_node = &self.root; // root is Option

        match *root_node {
            Some(ref node) => {

                if node.value == *key {
                    return true;
                }

                let target_node = if *key < node.value {
                    &node.left
                } else {
                    &node.right
                };

                match *target_node {
                    Some(sub_node) => sub_node.find(key),
                    None => {
                        return false;
                    } 
                }
            }
            None => return false,
        }
    }
}

fn main() {
    let mut mytree: Tree<i32> = Tree::new();

    let node1 = Node {
        value: 100,
        left: None,
        right: None,
    };
    let boxed_node1 = Some(Box::new(node1));

    let root = Node {
        value: 200,
        left: boxed_node1,
        right: None,
    };
    let boxed_root = Some(Box::new(root));
    let mytree = Tree { root: boxed_root };

    let res = mytree.find(&100);
}

The current code reports the error:

error: no method named `find` found for type `Box<Node<T>>` in the current scope
  --> src/main.rs:36:48
   |
36 |                     Some(sub_node) => sub_node.find(key),
   |                                                ^^^^
   |
   = note: the method `find` exists but the following trait bounds were not satisfied: `Node<T> : std::iter::Iterator`
   = help: items from traits can only be used if the trait is implemented and in scope; the following traits define an item `find`, perhaps you need to implement one of them:
   = help: candidate #1: `std::iter::Iterator`
   = help: candidate #2: `core::str::StrExt`

I understand that find is only implemented on Tree, so there is an error, but I don't think it is efficient to implement find on both Tree and Node. Any hint to solve this?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
enaJ
  • 1,565
  • 5
  • 16
  • 29

2 Answers2

2

Implement the find method on Node and create a stub find method for Tree which could look like this:

impl<T: Ord> Tree<T> {
    pub fn find(&self, key: &T) -> bool {
        match self.root.as_ref() {
            None => false,
            Some(x) => x.find(key)
        }
    }
}
belst
  • 2,285
  • 2
  • 20
  • 22
2

You need to move the majority of the implementation to the Node type, then leave only a small shim in Tree:

impl<T: Ord> Tree<T> {
    pub fn find(&self, key: &T) -> bool {
        self.root.as_ref().map(|n| n.find(key)).unwrap_or(false)
    }
}

impl<T: Ord> Node<T> {
    // search the tree
    pub fn find(&self, key: &T) -> bool {
        if self.value == *key {
            return true;
        }

        let target_node = if *key < self.value {
            &self.left
        } else {
            &self.right
        };

        target_node.as_ref().map(|n| n.find(key)).unwrap_or(false)
    }
}

However, I might avoid multiple comparisons by just matching on the result:

pub fn find(&self, key: &T) -> bool {
    use ::std::cmp::Ordering::*;

    match self.value.cmp(key) {
        Equal => true,
        Less => self.left.as_ref().map(|n| n.find(key)).unwrap_or(false),
        Greater => self.right.as_ref().map(|n| n.find(key)).unwrap_or(false),
    }
}

Or

pub fn find(&self, key: &T) -> bool {
    use ::std::cmp::Ordering::*;

    let child = match self.value.cmp(key) {
        Equal => return true,
        Less => self.left.as_ref(),
        Greater => self.right.as_ref(),
    };

    child.map(|n| n.find(key)).unwrap_or(false)
}

I found it is hard to understand target_node.as_ref().map(|n| n.find(key)).unwrap_or(false). I just started to learn the iterator. Is that possible to explain the long expression step by step?

Just follow the type signatures of each function:

  1. self is a &Node<T>
  2. &self.left / &self.right / target_node are a &Option<Box<Node<T>>>
  3. Option::as_ref converts an &Option<T> to Option<&T>. Now we have Option<&Box<Node<T>>>.
  4. Option::map applies a function (which may change the contained type) to the option if it is Some, otherwise it leaves it None.
    1. The function we apply is Node::find, which takes a &Node<T> and returns a bool.
    2. Box<T> implements Deref so any methods on T appear on Box<T>.
    3. Automatic dereferencing allows us to treat &Box<T> as Box<T>.
    4. Now we have Option<bool>
  5. Option::unwrap_or returns the contained value if there is one, otherwise the fallback value provided. The final type is bool.

There is no usage of the Iterator trait. Both Iterator and Option have a map method. If you are interested in the fact that they have the same name and do similar things, that [is what people refer to as a monad. Understanding monads is interesting but not required to actually use them.

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • For example, target_node.as_ref() convert a reference (&Tree) to ?? (&T) – enaJ Dec 03 '16 at 01:46
  • I deleted my first comment question since I felt bad about asking too many... Didn't expect you still see it and answer everything. Thanks! – enaJ Dec 03 '16 at 02:39