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,
)
| 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. |
| 131 | pub 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. |
| 178 | pub fn nonrecursive_dft_mut<Graph, NodeId, AtEnter, AtExit>( |
nothing calls this directly
no test coverage detected