New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should dfs be lazy? #918
Comments
@meooow25 , you might want to experiment with this. |
I assume you mean something like your #882 (comment)? I'll try it out. |
Yeah, something like that for the listy ones (if it's not too slow). I'm a bit more skeptical about |
Is there some actual reason for |
Oh, I see.... No. |
@meooow25 You had suggested dfs_preOrder :: Graph -> [Vertex] -> [Vertex]
dfs_preOrder g vs0 = run (bounds g) $ go vs0 (pure [])
where
go :: [Vertex] -> SetM s [Vertex] -> SetM s [Vertex]
go [] acc = acc
go (v:vs) acc = do
visited <- contains v
if visited
then go vs acc
else do
include v
as <- go (g!v) (go vs acc)
pure $ v : as This turns out not to be so good, I suspect because of the way the accumulator ends up being represented. I had better luck with this more explicit stack representation: dfs_preOrder :: Graph -> [Vertex] -> [Vertex]
dfs_preOrder g vs0 = run (bounds g) $ go vs0 []
where
go :: [Vertex] -> [[Vertex]] -> SetM s [Vertex]
go [] [] = pure []
go [] (x : xs) = go x xs
go (v:vs) acc = do
visited <- contains v
if visited
then go vs acc
else do
include v
as <- go (g!v) (vs : acc)
pure $ v : as The lazy version I came up with is a little slower to force fully, which I find a bit disappointing, but it may end up faster in a more complex context. lazy_dfs_preOrder :: Graph -> [Vertex] -> [Vertex]
lazy_dfs_preOrder g vs0 = run (bounds g) $ go vs0 []
where
go :: [Vertex] -> [[Vertex]] -> SetM s [Vertex]
go [] [] = pure []
go [] (x : xs) = go x xs
go (v:vs) acc = do
visited <- contains v
if visited
then go vs acc
else do
include v
as <- unsafeInterleaveSetM (go (g!v) (vs : acc))
pure $ v : as
unsafeInterleaveSetM :: SetM s a -> SetM s a
unsafeInterleaveSetM act = SetM $ \m -> unsafeInterleaveST (runSetM act m)
{-# INLINE unsafeInterleaveSetM #-} |
I tried to make the current dfs :: Graph -> [Vertex] -> Forest Vertex
dfs g vs0 = fst $ run (bounds g) $ go vs0
where
go :: [Vertex] -> SetM s (Forest Vertex, ())
go [] = pure ([], ())
go (v:vs) = do
visited <- contains v
if visited
then go vs
else do
include v
~(as, adone) <- unsafeInterleaveSetM $ go (g!v)
~(bs, bdone) <- unsafeInterleaveSetM $ adone `seq` go vs
pure (Node v as : bs, bdone)
Is there a better way to make a lazy implementation? I haven't tried lazy |
You've basically faked up the internal machinery of lazy |
The original paper we base
Data.Graph
on expects lazyST
, but we use strictST
. Depending how the result is consumed, lazier might be better. For ordered traversals (producing lists rather than trees/forests), we can use strictST
along withunsafeInterleaveST
, which may be faster.The text was updated successfully, but these errors were encountered: