Home > Software engineering >  Haskell: get all paths
Haskell: get all paths

Time:11-02

I'm looking for a smart way to get all paths traversing a given data structure. I figured out that I need a function like this:

allPaths :: (a -> [a]) -> a -> [[a]]

It takes a seed a and a function a -> [a] (from each "a", you can get to multiple a's). The result is a list of paths starting from the seed.

My definition is:

allPaths :: (a -> [a]) -> a -> [[a]]
allPaths f a = map (a:) (concatMap (allPaths f) (f a))

But it doesn't quite work:

ghci> let f a = if a == 1 then [2, 3] else if a == 2 then [4] else []
ghci> allPaths f 1
[]

The result should be: [[1,2,4], [1,3]], representing all the possible paths starting from 1.

CodePudding user response:

The problem is that as soon as you arrive at a [], you never prepend anything to that because there are no endpoints to walk towards.

Instead, [] should indicate that this is an endpoint.

allPaths f a = case f a of
  [] -> [[a]]
  fa -> map (a:) $ concatMap (allPaths f) fa

CodePudding user response:

As an alternative to the other answer, if you want all paths, not just all paths that arrive at a dead-end, then you need to prepend [] to the solution at each step, not just the final one.

allPaths :: (a -> [a]) -> a -> [[a]]
allPaths f a = map (a:) ([] : concatMap (allPaths f) (f a))

On your proposed f, this gives

*Main> allPaths f 1
[[1],[1,2],[1,2,4],[1,3]]

It includes the two paths leading to a dead end, as your example does, but it also includes [1] and [1,2], which are valid paths (they just aren't valid maximal paths). In particular, if your graph has cycles in it, then this approach will produce a valid infinite list of paths, whereas the other proposed answer will fail to produce any results as soon as it hits the cycle.

  • Related