@@ -53,11 +53,14 @@ pub fn walk(
5353 in graph : Graph ( n, e) ,
5454 using order : Order ,
5555) -> List ( NodeId ) {
56- case order {
57- BreadthFirst ->
58- do_walk_bfs ( graph , queue . new ( ) |> queue . push ( start_id ) , set . new ( ) , [ ] )
59- DepthFirst -> do_walk_dfs ( graph , [ start_id ] , set . new ( ) , [ ] )
60- }
56+ fold_walk (
57+ over : graph ,
58+ from : start_id ,
59+ using : order ,
60+ initial : [ ] ,
61+ with : fn ( acc , node_id , _meta ) { # ( Continue , [ node_id , .. acc ] ) } ,
62+ )
63+ |> list . reverse ( )
6164}
6265
6366/// Walks the graph but stops early when a condition is met.
@@ -82,18 +85,20 @@ pub fn walk_until(
8285 using order : Order ,
8386 until should_stop : fn ( NodeId ) -> Bool ,
8487) -> List ( NodeId ) {
85- case order {
86- BreadthFirst ->
87- do_walk_until_bfs (
88- graph ,
89- queue . new ( ) |> queue . push ( start_id ) ,
90- set . new ( ) ,
91- [ ] ,
92- should_stop ,
93- )
94- DepthFirst ->
95- do_walk_until_dfs ( graph , [ start_id ] , set . new ( ) , [ ] , should_stop )
96- }
88+ fold_walk (
89+ over : graph ,
90+ from : start_id ,
91+ using : order ,
92+ initial : [ ] ,
93+ with : fn ( acc , node_id , _meta ) {
94+ let new_acc = [ node_id , .. acc ]
95+ case should_stop ( node_id ) {
96+ True -> # ( Halt , new_acc )
97+ False -> # ( Continue , new_acc )
98+ }
99+ } ,
100+ )
101+ |> list . reverse ( )
97102}
98103
99104/// Folds over nodes during graph traversal, accumulating state with metadata.
@@ -212,132 +217,6 @@ pub fn fold_walk(
212217 }
213218}
214219
215- // BFS with efficient O(1) amortized queue operations
216- fn do_walk_bfs (
217- graph : Graph ( n, e) ,
218- q : queue . Queue ( NodeId ) ,
219- visited : Set ( NodeId ) ,
220- acc : List ( NodeId ) ,
221- ) -> List ( NodeId ) {
222- case queue . pop ( q ) {
223- Error ( Nil ) -> list . reverse ( acc )
224- Ok ( # ( head , rest ) ) -> {
225- case set . contains ( visited , head ) {
226- True -> do_walk_bfs ( graph , rest , visited , acc )
227- False -> {
228- let next_nodes = model . successor_ids ( graph , head )
229- let next_queue = queue . push_list ( rest , next_nodes )
230-
231- do_walk_bfs ( graph , next_queue , set . insert ( visited , head ) , [
232- head ,
233- .. acc
234- ] )
235- }
236- }
237- }
238- }
239- }
240-
241- // DFS with list-based stack (prepend is O(1))
242- fn do_walk_dfs (
243- graph : Graph ( n, e) ,
244- stack : List ( NodeId ) ,
245- visited : Set ( NodeId ) ,
246- acc : List ( NodeId ) ,
247- ) -> List ( NodeId ) {
248- case stack {
249- [ ] -> list . reverse ( acc )
250- [ head , .. tail ] -> {
251- case set . contains ( visited , head ) {
252- True -> do_walk_dfs ( graph , tail , visited , acc )
253- False -> {
254- let next_nodes = model . successor_ids ( graph , head )
255- let next_stack = list . append ( next_nodes , tail )
256-
257- do_walk_dfs ( graph , next_stack , set . insert ( visited , head ) , [
258- head ,
259- .. acc
260- ] )
261- }
262- }
263- }
264- }
265- }
266-
267- // BFS with early termination
268- fn do_walk_until_bfs (
269- graph : Graph ( n, e) ,
270- q : queue . Queue ( NodeId ) ,
271- visited : Set ( NodeId ) ,
272- acc : List ( NodeId ) ,
273- should_stop : fn ( NodeId ) -> Bool ,
274- ) -> List ( NodeId ) {
275- case queue . pop ( q ) {
276- Error ( Nil ) -> list . reverse ( acc )
277- Ok ( # ( head , rest ) ) -> {
278- case set . contains ( visited , head ) {
279- True -> do_walk_until_bfs ( graph , rest , visited , acc , should_stop )
280- False -> {
281- let current_acc = [ head , .. acc ]
282-
283- case should_stop ( head ) {
284- True -> list . reverse ( current_acc )
285- False -> {
286- let next_nodes = model . successor_ids ( graph , head )
287- let next_queue = queue . push_list ( rest , next_nodes )
288-
289- do_walk_until_bfs (
290- graph ,
291- next_queue ,
292- set . insert ( visited , head ) ,
293- current_acc ,
294- should_stop ,
295- )
296- }
297- }
298- }
299- }
300- }
301- }
302- }
303-
304- // DFS with early termination
305- fn do_walk_until_dfs (
306- graph : Graph ( n, e) ,
307- stack : List ( NodeId ) ,
308- visited : Set ( NodeId ) ,
309- acc : List ( NodeId ) ,
310- should_stop : fn ( NodeId ) -> Bool ,
311- ) -> List ( NodeId ) {
312- case stack {
313- [ ] -> list . reverse ( acc )
314- [ head , .. tail ] -> {
315- case set . contains ( visited , head ) {
316- True -> do_walk_until_dfs ( graph , tail , visited , acc , should_stop )
317- False -> {
318- let current_acc = [ head , .. acc ]
319-
320- case should_stop ( head ) {
321- True -> list . reverse ( current_acc )
322- False -> {
323- let next_nodes = model . successor_ids ( graph , head )
324- let next_stack = list . append ( next_nodes , tail )
325-
326- do_walk_until_dfs (
327- graph ,
328- next_stack ,
329- set . insert ( visited , head ) ,
330- current_acc ,
331- should_stop ,
332- )
333- }
334- }
335- }
336- }
337- }
338- }
339- }
340-
341220// BFS with fold and metadata
342221fn do_fold_walk_bfs (
343222 graph : Graph ( n, e) ,
@@ -405,15 +284,20 @@ fn do_fold_walk_dfs(
405284 Continue -> {
406285 // Add successors to stack with updated metadata
407286 let next_nodes = model . successor_ids ( graph , node_id )
287+ // Reverse to maintain correct DFS order (since we prepend during fold)
408288 let next_stack =
409- list . fold ( next_nodes , tail , fn ( current_stack , next_id ) {
410- let next_meta =
411- WalkMetadata (
412- depth : metadata . depth + 1 ,
413- parent : Some ( node_id ) ,
414- )
415- [ # ( next_id , next_meta ) , .. current_stack ]
416- } )
289+ list . fold (
290+ list . reverse ( next_nodes ) ,
291+ tail ,
292+ fn ( current_stack , next_id ) {
293+ let next_meta =
294+ WalkMetadata (
295+ depth : metadata . depth + 1 ,
296+ parent : Some ( node_id ) ,
297+ )
298+ [ # ( next_id , next_meta ) , .. current_stack ]
299+ } ,
300+ )
417301
418302 do_fold_walk_dfs ( graph , next_stack , new_visited , new_acc , folder )
419303 }
0 commit comments