Haskell中的漂亮打印表达结构?

时间:2014-03-31 02:05:03

标签: haskell tree pretty-print

我们为我们的任务提供了以下数据结构:

-- Question 2: Expression tree.
data Expr
  = Lit Float
  | Add Expr Expr
  | Sub Expr Expr
  | Mul Expr Expr
  | Div Expr Expr
  | X

表示浮点值,两个子树的add / sub / mul / div,或者表示未知变量的X.我们必须编写一个可以打印树的方法。这就是我到目前为止所做的:

draw :: Expr -> Int -> String
draw (Lit f) _ = show f
draw (X) _ = "X"
draw (Add a b) lvl = indent lvl ++ "(+) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ "|\n" ++ indent lvl ++ "---" ++ draw b (lvl+1) 
draw (Sub a b) lvl = indent lvl ++ "(-) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ "|\n" ++ indent lvl ++ "---" ++ draw b (lvl+1) 
draw (Mul a b) lvl = indent lvl ++ "(*) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ "|\n" ++ indent lvl ++ "---" ++ draw b (lvl+1) 
draw (Div a b) lvl = indent lvl ++ "(/) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ "|\n" ++ indent lvl ++ "---" ++ draw b (lvl+1) 

indent :: Int -> [Char]
indent 0 = []
indent n = "\t"++indent (n-1)

这适用于简单的树:

myE2 :: Expr
myE2 = Add (Lit 4) (Sub (Lit 4) (Lit 2))

打印出:

*Main> putStr(draw myE2 0)
(+) ---4.0
|
--- (-) ---4.0
     |
     ---2.0

然而,这是我想要的更复杂的树,例如:

myExpr :: Expr
myExpr = (Add (Add (Sub (Mul (Lit 4) (X)) (Lit 1)) (Lit 4)) (Lit 6))

打印为:

*Main> putStr(draw myExpr 0)
(+) --- (+) ---     (-) ---         (*) ---4.0
            |
            ---X
        |
        ---1.0
    |
    ---4.0
|
---6.0

有人可以就如何解决这个问题提出建议吗?

2 个答案:

答案 0 :(得分:3)

首先,您是否必须使用当前使用的表达式格式?此格式要求将许多节点打印到一行。如果查看Data.Tree.Pretty使用的格式,它只会将单个节点打印到任何行。如果您以这种方式格式化代码,myExpr将采用如下格式:

(+)
 |
 +-(+)
 |  |
 |  +-(-)
 |  |  |
 |  |  +-(*)
 |  |  |  |
 |  |  |  +-4
 |  |  |  |
 |  |  |  +-X
 |  |  |
 |  |  +-1
 |  |
 |  +-4
 |
 +-6

这使得问题更加严重,很多更简单,因为每个节点都有一行。打印节点所需要做的就是知道它在树中的深度。例如,节点3深将具有以" | | +-"开头的行。然后从左到右打印每个节点,您将得到结果。如果您遇到困难,请尝试查看source for the Data.Tree.drawTree以获得灵感。

如果您必须坚持使用当前格式,请做好大量工作的准备。您必须根据每个节点中文本的宽度动态布局树。您还需要展开节点,以免出现重叠。在开始之前,您需要考虑整棵树。这是NP完全问题。有关更多详细信息,请参阅this tree article。这肯定超出了家庭作业的问题......

答案 1 :(得分:1)

您可以尝试以下操作:

data Expr
  = Lit Float
  | Add Expr Expr
  | Sub Expr Expr
  | Mul Expr Expr
  | Div Expr Expr
  | X

draw :: Expr -> Int -> String
draw (Lit f) _ = show f
draw (X) _ = "X"
draw (Add a b) lvl = "(+) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ " |\n" ++ indent lvl ++ " ------" ++ draw b (lvl+1) 
draw (Sub a b) lvl = "(-) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ " |\n" ++ indent lvl ++ " ------" ++ draw b (lvl+1) 
draw (Mul a b) lvl = "(*) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ " |\n" ++ indent lvl ++ " ------" ++ draw b (lvl+1) 
draw (Div a b) lvl = "(/) ---" ++ draw a (lvl+1) ++ "\n" ++ indent lvl ++ " |\n" ++ indent lvl ++ " ------" ++ draw b (lvl+1) 

indent :: Int -> [Char]
indent 0 = []
indent n = "       " ++ indent (n-1)

myE2 = Add (Lit 4) (Sub (Lit 4) (Lit 2))
myE3 = Add (Add (Lit 4) (Lit 3)) (Sub (Lit 4) (Lit 2))
myE4 = Add (Add (Lit 4) (Add (Lit 4) (Sub (Lit 4) (Lit 2)))) (Sub (Lit 4) (Lit 2))
myExpr = (Add (Add (Sub (Mul (Lit 4) (X)) (Lit 1)) (Lit 4)) (Lit 6))

它适用于我使用的不同条件。主要变化: 我将“\ t”更改为spcaes。 我删除了第一个缩进。

[ghci] putStrLn $ draw myExpr 0
(+) ---(+) ---(-) ---(*) ---4.0
                      |
                      ------X
               |
               ------1.0
        |
        ------4.0
 |
 ------6.0

[ghci] putStrLn $ draw myE4 0
(+) ---(+) ---4.0
        |
        ------(+) ---4.0
               |
               ------(-) ---4.0
                      |
                      ------2.0
 |
 ------(-) ---4.0
        |
        ------2.0

[ghci] putStrLn $ draw myE3 0
(+) ---(+) ---4.0
        |
        ------3.0
 |
 ------(-) ---4.0
        |
        ------2.0