Add a function to return the list of edges in a graph#17
Add a function to return the list of edges in a graph#17garyb merged 9 commits intopurescript:masterfrom MaybeJustJames:add-edges-function
Conversation
|
I think this shouldn't add a dependency on |
|
@JordanMartinez that was significantly easier than I expected. I also exposed the |
| unfoldGraph ks label theEdges = | ||
| 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.
Could you revert these two changes since they're not relevant to this PR?
There was a problem hiding this comment.
These 2 changes are to avoid shadowing the edges name
There was a problem hiding this comment.
Ah! Makes sense. Ok.
| -- | 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.
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 _ deststores. 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 destsSecond, 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.
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
| 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.
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.
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?
JordanMartinez
left a comment
There was a problem hiding this comment.
LGTM besides the changelog issue.
|
|
||
| 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.
Could you put this back into the correct spot in the changelog?
There was a problem hiding this comment.
I moved this because it hasn't been released. Would you prefer a separate PR?
There was a problem hiding this comment.
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 |
garyb
left a comment
There was a problem hiding this comment.
👍 Thanks for making the change!
Adds an
edgesfunction and a correspondingEdgetype.Reasoning for the change:
I had implemented this badly in my own project.
edgesfunctionality is also necessary to implement anEqinstance forGraphif that should be considered (RE #1).Checklist: