-
Notifications
You must be signed in to change notification settings - Fork 10
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
Add a function to return the list of edges in a graph #17
Conversation
I think this shouldn't add a dependency on |
@JordanMartinez that was significantly easier than I expected. I also exposed the |
Graph (M.fromFoldable (map (\k -> | ||
Tuple k (Tuple (label k) (L.fromFoldable (edges k)))) ks)) | ||
Tuple k (Tuple (label k) (L.fromFoldable (theEdges k)))) ks)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you revert these two changes since they're not relevant to this PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These 2 changes are to avoid shadowing the edges
name
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! Makes sense. Ok.
src/Data/Graph.purs
Outdated
-- | List all edges in a graph | ||
edges :: forall k v. Graph k v -> List (Edge k) | ||
edges (Graph g) = go initialState | ||
where | ||
go :: EdgesState k v -> List (Edge k) | ||
go { unvisited: Nil, result: res } = res | ||
go { unvisited: (src /\ (_ /\ dests)) : ns, result: res } = | ||
go | ||
{ unvisited: ns | ||
, result: map (Edge <<< (src /\ _)) dests <> res | ||
} | ||
|
||
initialState :: EdgesState k v | ||
initialState = | ||
{ unvisited: M.toUnfoldableUnordered g | ||
, result: Nil | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm pretty sure this is slow for a few reasons.
First, you're using List
and you have at least two traversals over dests
:
map (Edge <<< (src /\ _)) dests
: 1 traversal to create a new version of the list(map _ dests) <> res
: 1 traversal appending the result ofmap _ dests
tores
. See https://github.com/JordanMartinez/purescript-jordans-reference/blob/latestRelease/21-Hello-World/05-Application-Structure/src/03-Free/01-What-Is-The-Free-Monad/04-The-Remorseless-Free-Monad.md#the-direction-matters for context.
A faster solution would to fold over dests
and cons the src
and destination
onto res
in one fold:
result: foldl (\acc dest -> Edge (src /\ dest) : acc) res dests
Second, for initialState
, why convert the Map
to a List
in the first place via M.toUnfoldableUnordered
? That traverses the map once and then you traverse the map a second time in go
via the unvisited
field. Since Graph
is just a newtype around Map
and Map
has an instance for FoldableWithIndex
, couldn't this same key-value information be obtained with foldlWithIndex
using a single traversal rather than two as it currently does?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wow! I did not notice that at all. Thank you for taking the time to explain this all to me so clearly :)
As you mentioned I've replaced the custom function with foldlWithIndex
(which traverses the Map
once) and a foldl
for each list of edge endpoints. So I cannot see any redundant traversals now.
src/Data/Graph.purs
Outdated
@@ -42,6 +45,9 @@ instance traversableGraph :: Traversable (Graph k) where | |||
traverse f (Graph m) = Graph <$> (traverse (\(v /\ ks) -> (_ /\ ks) <$> (f v)) m) | |||
sequence = traverse identity | |||
|
|||
-- | An edge within a `Graph` referencing endpoint keys | |||
newtype Edge k = Edge (Tuple k k) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rather than defining an edge, it may be more helpful to define a newtype indicating which value inside the Tuple is the source and which is the destination. For example:
-newtype Edge k = Edge (Tuple k k)
+Tuple (Source k) (Destination k)
I'm not sure what the best API would be here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've implemented this. I don't think it's any less ergonomic and, as you say, prevents mixing up source and destination. What do you think? Is this what you meant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM besides the changelog issue.
@@ -19,7 +21,6 @@ Breaking changes: | |||
|
|||
New features: | |||
- Added `Foldable` and `Traversable` instances for `Graph` (#16 by @MaybeJustJames) | |||
- Added `toMap` to unwrap `Graph` (#18) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you put this back into the correct spot in the changelog?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved this because it hasn't been released. Would you prefer a separate PR?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, my bad. No, let's just fix it here.
Can this be merged? |
@garyb Can this get an approval? |
Another option here might be to use If we don't want to go that route, then I'd suggest including |
I've just pushed this. It still has the advantage of not mixing up source and destination without needing |
🏓 @garyb |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Thanks for making the change!
Adds an
edges
function and a correspondingEdge
type.Reasoning for the change:
I had implemented this badly in my own project.
edges
functionality is also necessary to implement anEq
instance forGraph
if that should be considered (RE #1).Checklist: