How do you represent a graph in Haskell?

后端 未结 8 726
星月不相逢
星月不相逢 2020-11-27 09:39

It\'s easy enough to represent a tree or list in haskell using algebraic data types. But how would you go about typographically representing a graph? It seems that you need

8条回答
  •  渐次进展
    2020-11-27 10:20

    Any discussion of representing graphs in Haskell needs a mention of Andy Gill's data-reify library (here is the paper).

    The "tying-the-knot" style representation can be used to make very elegant DSLs (see example below). However, the data structure is of limited use. Gill's library allows you the best of both worlds. You can use a "tying the knot" DSL, but then convert the pointer-based graph into a label-based graph so you can run your algorithms of choice on it.

    Here is a simple example:

    -- Graph we want to represent:
    --    .----> a <----.
    --   /               \
    --  b <------------.  \
    --   \              \ / 
    --    `----> c ----> d
    
    -- Code for the graph:
    a = leaf
    b = node2 a c
    c = node1 d
    d = node2 a b
    -- Yes, it's that simple!
    
    
    
    -- If you want to convert the graph to a Node-Label format:
    main = do
        g <- reifyGraph b   --can't use 'a' because not all nodes are reachable
        print g
    

    To run the above code you will need the following definitions:

    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE TypeFamilies #-}
    import Data.Reify
    import Control.Applicative
    import Data.Traversable
    
    --Pointer-based graph representation
    data PtrNode = PtrNode [PtrNode]
    
    --Label-based graph representation
    data LblNode lbl = LblNode [lbl] deriving Show
    
    --Convenience functions for our DSL
    leaf      = PtrNode []
    node1 a   = PtrNode [a]
    node2 a b = PtrNode [a, b]
    
    
    -- This looks scary but we're just telling data-reify where the pointers are
    -- in our graph representation so they can be turned to labels
    instance MuRef PtrNode where
        type DeRef PtrNode = LblNode
        mapDeRef f (PtrNode as) = LblNode <$> (traverse f as)
    

    I want to stress that this is a simplistic DSL, but the sky's the limit! I designed a very featureful DSL, including a nice tree-like syntax for having a node broadcast an initial value to some of its children, and many convenience functions for constructing specific node types. Of course, the Node data type and mapDeRef definitions were much more involved.

提交回复
热议问题