В целом, я думаю, что ваше решение выглядит довольно хорошо - я думаю, что это та проблема, в которой немного изменчивости делает выражение алгоритма более понятным. Однако вместо использования изменяемой ссылки на неизменяемый набор, который вы обновляете неоднократно, лучше использовать реализацию изменяемого набора:
open System.Collections.Generic
let getConnected node edges =
let visited = HashSet()
let rec traverse node =
if not (visited.Contains node) then
visited.Add node
directEdges node edges
|> List.iter (fun (a,b) -> traverse b)
traverse node
visited
и если вы хотите, чтобы вывод был неизменным, вы можете просто добавить |> set
после последней строки.
С другой стороны, если вы хотите использовать функциональный подход, это не слишком сложно:
let getConnected node edges =
let rec traverse node visited =
if not (Set.contains node visited) then
directEdges node edges
|> List.fold (fun nodes (a, b) -> traverse b nodes) (Set.add node visited)
else
visited
traverse node Set.empty