MCPcopy Create free account
hub / github.com/MaterializeInc/materialize / try_nonrecursive_dft

Function try_nonrecursive_dft

src/ore/src/graph.rs:36–82  ·  view source on GitHub ↗

A non-recursive implementation of a fallible depth-first traversal starting from `root`. Assumes that nodes in the graph all have unique node ids. `at_enter` runs when entering a node. It is expected to return an in-order list of the children of the node. You can omit children from the list returned if you want to skip traversing the subgraphs corresponding to those children. If no children are

(
    graph: &Graph,
    root: NodeId,
    at_enter: &mut AtEnter,
    at_exit: &mut AtExit,
)

Source from the content-addressed store, hash-verified

34/// This function only enters and exits a node at most once and thus is safe to
35/// run even if the graph contains a cycle.
36pub fn try_nonrecursive_dft<Graph, NodeId, AtEnter, AtExit, E>(
37 graph: &Graph,
38 root: NodeId,
39 at_enter: &mut AtEnter,
40 at_exit: &mut AtExit,
41) -> Result<(), E>
42where
43 NodeId: std::cmp::Ord,
44 AtEnter: FnMut(&Graph, &NodeId) -> Result<Vec<NodeId>, E>,
45 AtExit: FnMut(&Graph, &NodeId) -> Result<(), E>,
46{
47 // All nodes that have been entered but not exited. Last node in the vec is
48 // the node that we most recently entered.
49 let mut entered = Vec::new();
50 // All nodes that have been exited.
51 let mut exited = BTreeSet::new();
52
53 // Pseudocode for the recursive version of this function would look like:
54 // ```
55 // children = at_enter(graph, node)
56 // foreach child in children:
57 // recursive_call(graph, child)
58 // atexit(graph, node)
59 // ```
60 // In this non-recursive implementation, you can think of the call stack as
61 // been replaced by `entered`. Every time an object is pushed into `entered`
62 // would have been a time you would have pushed a recursive call onto the
63 // call stack. Likewise, times an object is popped from `entered` would have
64 // been times when recursive calls leave the stack.
65
66 // Enter from the root.
67 let children = at_enter(graph, &root)?;
68 entered_node(&mut entered, root, children);
69 while !entered.is_empty() {
70 if let Some(to_enter) = find_next_child_to_enter(&mut entered, &exited) {
71 let children = at_enter(graph, &to_enter)?;
72 entered_node(&mut entered, to_enter, children);
73 } else {
74 // If this node has no more children to descend into,
75 // exit the current node and run `at_exit`.
76 let (to_exit, _) = entered.pop().unwrap();
77 at_exit(graph, &to_exit)?;
78 exited.insert(to_exit);
79 }
80 }
81 Ok(())
82}
83
84/// Same as [`try_nonrecursive_dft`], but allows changes to be made to the graph.
85pub fn try_nonrecursive_dft_mut<Graph, NodeId, AtEnter, AtExit, E>(

Callers

nothing calls this directly

Calls 6

entered_nodeFunction · 0.85
find_next_child_to_enterFunction · 0.85
unwrapMethod · 0.80
is_emptyMethod · 0.45
popMethod · 0.45
insertMethod · 0.45

Tested by

no test coverage detected