[Haskell-cafe] Draw K-ary forest in dot script
larry.liuxinyu
liuxinyu95 at gmail.com
Mon Jan 10 07:33:03 CET 2011
Hi,
I wrote a Haskell program to parse K-ary forest and convert it to dot script
(Graphviz).
Here is the literate program.
-- First is some stuff imported:
module Main where
import System.Environment (getArgs)
import Text.ParserCombinators.Parsec
import Control.Monad (mapM_)
import Data.List (concatMap, intercalate)
import System.IO (writeFile)
import Data.Char (isSpace)
-- For each tree in the forest, it is described in pre-order.
-- Example description string of a forest of CLRS[1] Figure 19.5(a):
-- (12), (7, (25)), (15, (28, (41)), (33))
-- Definition of K-ary node
data Node a = Node { root :: a
, children :: [Node a]} deriving (Eq, Show)
-- Definition of Forest
type Forest a = [Node a]
-- parsers
-- a forest is a list of trees separate by ','
forest = do
ts <- node `sepBy` (char ',')
return ts
-- a node contains a key then followed by a children forest or nothing (leaf
case)
node = do
char '('
elem <- key
ts <- (try (char ',')>>forest) <|> return []
char ')'
return (Node elem ts)
-- a key is just a plain literate string.
key = many (noneOf ",()")
-- Command line arguments handling
parseArgs :: [String] -> (String, String)
parseArgs [fname, s] = (fname, s)
parseArgs _ = error "wrong usage\nexample:\nfr2dot output.dot \"(12), (7,
(25)), (15, ((28, (41)), 33))\""
-- A simplified function to generate dot script from parsed result.
toDot f = forestToDot f "t" True
-- a handy function to convert children of a K-ary tree to dot script
treesToDot ts prefix = forestToDot ts prefix False
-- convert a forest to dot script
forestToDot [] _ _ = ""
forestToDot [t] prefix _ = nodeToDot t prefix
forestToDot ts@(_:_:_) prefix lnk =
(concatMap (\t->nodeToDot t prefix) ts) ++ consRoot
where
consRoot = "{rank=same " ++ ns ++ vis ++ "}\n"
ns = intercalate "->" $ map (\t -> prefix ++ root t) ts
vis = if lnk then "" else "[style=invis]"
-- convert a node to dot script
nodeToDot (Node x ts) prefix =
prefix'++"[label=\""++x++"\"];\n" ++
(treesToDot ts prefix') ++
(defCons ts prefix')
where prefix' = prefix ++ x
-- define connections among nodes in dot format
defCons ts prefix = concatMap f ts where
f (Node x _) = prefix++"->"++prefix++x++";\n"
-- generate dot script from a parsed forest
genDot fname (Right f) = writeFile fname dots >> putStrLn dots
where
dots = "digraph G{\n\tnode[shape=circle]\n"++(addTab $ toDot f)++"}"
addTab s = unlines $ map ("\t"++) (lines s)
main = do
args <- getArgs
let (fname, s) = parseArgs args
genDot fname (parse forest "unknown" (filter (not.isSpace) s))
-- END
I tested with the following simple cases:
./fr2dot foo.dot "(12), (7, (25)), (15, (28, (41)), (33))"
./fr2dot bar.dot "(18), (3, (37)), (6, (8, (30, (45, (55)), (32)), (23,
(24)), (22)), (29, (48, (50)), (31)), (10, (17)), (44))"
Run the following commands can convert to PNG files:
./dot -Tpng -o foo.png foo.dot
./dot -Tpng -o bar.png bar.dot
Reference:
[1]. Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest and Clifford
Stein. ``Introduction to Algorithms, Second Edition''. The MIT Press, 2001.
ISBN: 0262032937.
Best regards.
--
Larry, LIU
https://sites.google.com/site/algoxy/home
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.haskell.org/pipermail/haskell-cafe/attachments/20110109/cacce0d8/attachment.htm>
More information about the Haskell-Cafe
mailing list