Haskell show instance on list

2020-04-14 02:58发布

问题:

Im having issues with adding a show instance to my data structure, wich is:

data Structure = Structure String [Structure]  

and I would like to have this output:

strct  
    strct1  
    strct2  
    strct3  

I've been trying this

instance Show Structure where  
    show (Structure a (xs)) = show a ++ "\n" ++ "  " ++ show xs  

But its output is

"strct"  
    ["strct1"  
    [], "strct2"  
    []]  

So, I would need no brackets, no commas and no quotation marks. Any ideas? Thanks

回答1:

I'm sure there are better library routines for this, but wouldn't this work?

unlines $ a : ["  " ++ show x | x <- xs]

However, that covers only one level. You probably want to define a different function than show to maintain the indentation, or you'd have to keep splitting sub-shows with lines to find where to inject indentation.

A rough draft of such an indentation insertion function is:

prefix p s = unlines [p ++ l | l <- lines s]

Again, I'm sure there's something better in a library. Even these short snippets have gone through a few steps of refinement (foldl1 (++) -> concat -> unlines, then join the first line as head with :).



回答2:

Basically there only one bookkeeping to do, i.e. keep tract of the indentation level. An accumulative counter definitely helps:

pretty' :: Int -> Structure -> [String]
pretty' level (Structure s list) = ((concat $ replicate level "    ") ++ s) :
    (list >>= (pretty' (level + 1)))

This function defines an accumulator of your pretty printer. The remaining part is to wrap it:

pretty = pretty' 0
prettyShow = unlines . pretty

It definitely works

Prelude> putStrLn $ prettyShow (Structure "a" [Structure "b" [Structure "c" []], Structure "d" []])
a
    b
        c
    d


回答3:

I would suggest using the Data.Tree module that comes with base. It is a generic (rose) tree with a structure identical to yours, except that the list goes by a type synonym:

data Tree a = Node {rootLabel :: a, subForest :: Forest a}
type Forest a = [Tree a]

It also comes with the drawTree and drawForest functions which are roughly what you were trying to write yourself.

> :m + Data.Tree
> let t = Node "strct" [Node "strct1" [], Node "strct2" [], Node "strct3" []]
> putStr . drawTree $ t
strct
|
+- strct1
|
+- strct2
|
`- strct3