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

Function nonrecursive_dft

src/ore/src/graph.rs:131–175  ·  view source on GitHub ↗

A non-recursive implementation of an infallible 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 the traversing subgraphs corresponding to those children. If no children a

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

Source from the content-addressed store, hash-verified

129/// This function only enters and exits a node at most once and thus is safe to
130/// run even if the graph contains a cycle.
131pub fn nonrecursive_dft<Graph, NodeId, AtEnter, AtExit>(
132 graph: &Graph,
133 root: NodeId,
134 at_enter: &mut AtEnter,
135 at_exit: &mut AtExit,
136) where
137 NodeId: std::cmp::Ord,
138 AtEnter: FnMut(&Graph, &NodeId) -> Vec<NodeId>,
139 AtExit: FnMut(&Graph, &NodeId) -> (),
140{
141 // All nodes that have been entered but not exited. Last node in the vec is
142 // the node that we most recently entered.
143 let mut entered = Vec::new();
144 // All nodes that have been exited.
145 let mut exited = BTreeSet::new();
146
147 // Pseudocode for the recursive version of this function would look like:
148 // ```
149 // atenter(graph, node)
150 // foreach child in children(graph, node):
151 // recursive_call(graph, child)
152 // atexit(graph, node)
153 // ```
154 // In this non-recursive implementation, you can think of the call stack as
155 // been replaced by `entered`. Every time an object is pushed into `entered`
156 // would have been a time you would have pushed a recursive call onto the
157 // call stack. Likewise, times an object is popped from `entered` would have
158 // been times when recursive calls leave the stack.
159
160 // Enter from the root.
161 let children = at_enter(graph, &root);
162 entered_node(&mut entered, root, children);
163 while !entered.is_empty() {
164 if let Some(to_enter) = find_next_child_to_enter(&mut entered, &exited) {
165 let children = at_enter(graph, &to_enter);
166 entered_node(&mut entered, to_enter, children);
167 } else {
168 // If this node has no more children to descend into,
169 // exit the current node and run `at_exit`.
170 let (to_exit, _) = entered.pop().unwrap();
171 at_exit(graph, &to_exit);
172 exited.insert(to_exit);
173 }
174 }
175}
176
177/// Same as [`nonrecursive_dft`], but allows changes to be made to the graph.
178pub fn nonrecursive_dft_mut<Graph, NodeId, AtEnter, AtExit>(

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