diff --git a/src/Algebra/Graph.hs b/src/Algebra/Graph.hs index ff4327d85..f7d297b86 100644 --- a/src/Algebra/Graph.hs +++ b/src/Algebra/Graph.hs @@ -57,6 +57,7 @@ import Control.Applicative (Alternative) import Control.DeepSeq (NFData (..)) import Control.Monad.Compat import Data.Foldable (toList) +import Data.Maybe (fromMaybe) import Data.Tree import Algebra.Graph.Internal @@ -317,7 +318,7 @@ edges = overlays . map (uncurry edge) -- 'isEmpty' . overlays == 'all' 'isEmpty' -- @ overlays :: [Graph a] -> Graph a -overlays = foldr overlay empty +overlays = concatg overlay -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length @@ -331,7 +332,11 @@ overlays = foldr overlay empty -- 'isEmpty' . connects == 'all' 'isEmpty' -- @ connects :: [Graph a] -> Graph a -connects = foldr connect empty +connects = concatg connect + +-- | Auxiliary function, similar to 'mconcat'. +concatg :: (Graph a -> Graph a -> Graph a) -> [Graph a] -> Graph a +concatg combine = fromMaybe empty . foldr1Safe combine -- | Generalised 'Graph' folding: recursively collapse a 'Graph' by applying -- the provided functions to the leaves and internal nodes of the expression. diff --git a/src/Algebra/Graph/Internal.hs b/src/Algebra/Graph/Internal.hs index eacb3952d..2d83afa22 100644 --- a/src/Algebra/Graph/Internal.hs +++ b/src/Algebra/Graph/Internal.hs @@ -20,7 +20,9 @@ module Algebra.Graph.Internal ( List (..), -- * Data structures for graph traversal - Focus (..), emptyFocus, vertexFocus, overlayFoci, connectFoci, Hit (..) + Focus (..), emptyFocus, vertexFocus, overlayFoci, connectFoci, Hit (..), + + foldr1Safe ) where import Prelude () @@ -107,3 +109,12 @@ connectFoci x y = Focus (ok x || ok y) (xs <> is y) (os x <> ys) (vs x <> vs y) -- | An auxiliary data type for 'hasEdge': when searching for an edge, we can hit -- its 'Tail', i.e. the source vertex, the whole 'Edge', or 'Miss' it entirely. data Hit = Miss | Tail | Edge deriving (Eq, Ord) + +-- | A safe version of 'foldr1' +foldr1Safe :: (a -> a -> a) -> [a] -> Maybe a +foldr1Safe f = foldr mf Nothing + where + mf x m = Just (case m of + Nothing -> x + Just y -> f x y) +{-# INLINE foldr1Safe #-} diff --git a/src/Algebra/Graph/NonEmpty.hs b/src/Algebra/Graph/NonEmpty.hs index e94946e2e..d2911c452 100644 --- a/src/Algebra/Graph/NonEmpty.hs +++ b/src/Algebra/Graph/NonEmpty.hs @@ -279,7 +279,7 @@ connect = Connect -- 'vertexSet' . vertices1 == Set.'Set.fromList' . 'Data.List.NonEmpty.toList' -- @ vertices1 :: NonEmpty a -> NonEmptyGraph a -vertices1 (x :| xs) = foldr (Overlay . vertex) (vertex x) xs +vertices1 = overlays1 . fmap vertex -- | Construct the graph from a list of edges. -- Complexity: /O(L)/ time, memory and size, where /L/ is the length of the @@ -290,7 +290,7 @@ vertices1 (x :| xs) = foldr (Overlay . vertex) (vertex x) xs -- 'edgeCount' . edges1 == 'Data.List.NonEmpty.length' . 'Data.List.NonEmpty.nub' -- @ edges1 :: NonEmpty (a, a) -> NonEmptyGraph a -edges1 (x :| xs) = foldr (Overlay . uncurry edge) (uncurry edge x) xs +edges1 = overlays1 . fmap (uncurry edge) -- | Overlay a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length @@ -301,7 +301,7 @@ edges1 (x :| xs) = foldr (Overlay . uncurry edge) (uncurry edge x) xs -- overlays1 (x ':|' [y]) == 'overlay' x y -- @ overlays1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a -overlays1 = foldr1 overlay +overlays1 = concatg1 overlay -- | Connect a given list of graphs. -- Complexity: /O(L)/ time and memory, and /O(S)/ size, where /L/ is the length @@ -312,7 +312,11 @@ overlays1 = foldr1 overlay -- connects1 (x ':|' [y]) == 'connect' x y -- @ connects1 :: NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a -connects1 = foldr1 connect +connects1 = concatg1 connect + +-- | Auxiliary function, similar to 'sconcat'. +concatg1 :: (NonEmptyGraph a -> NonEmptyGraph a -> NonEmptyGraph a) -> NonEmpty (NonEmptyGraph a) -> NonEmptyGraph a +concatg1 combine (x :| xs) = maybe x (combine x) $ foldr1Safe combine xs -- | Generalised graph folding: recursively collapse a 'NonEmptyGraph' by -- applying the provided functions to the leaves and internal nodes of the