我目前有以下实现,但我想知道是否有更快或不使用显式递归的解决方案:
import Data.Set (Set)
import qualified Data.Set as Set
reachable :: Ord node => Set node -> (node -> Set node) -> Set node
reachable startingNodes successors =
go startingNodes Set.empty
where
go todo checked =
case Set.minView todo of
Nothing -> checked
Just (node, todo') ->
let new = successors node Set.\\ checked
todo'' = todo' `Set.union` new
checked' = Set.insert node checked
in go todo'' checked'
答案 0 :(得分:1)
您没有包含测试用例,所以我没有在任何示例上运行此解决方案:一些错误可能就在这里和那里。但是这样的事情应该有效:
编写一个函数step
,在探索的子图的边界上给定一组访问节点和一组节点,在该边界的所有方向上迈出一步。结果是一组新的访问节点和一个新的边界。
使用unfoldr
重复应用此功能,从而获得(增长的)受访节点集的列表。闭包只是该列表的last
元素。
以下代码的作用如下:
{-# LANGUAGE ScopedTypeVariables #-}
module Reachability where
import Control.Monad (guard)
import Data.List (unfoldr)
import Data.Set (Set, union, empty, (\\))
import qualified Data.Set as Set
reachable' :: forall node. Ord node => Set node -> (node -> Set node) -> Set node
reachable' seed next = last $ seed : unfoldr step (seed, empty) where
step :: (Set node, Set node) -> Maybe (Set node, (Set node, Set node))
step (seed, visited) = guard (not $ null seed) >>
let successors = foldr (union . next) empty seed
newVisited = visited `union` seed
newSeed = successors \\ newVisited
in return (newVisited, (newSeed, newVisited))