我们为我们的任务提供了以下数据结构:
-- 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
有人可以就如何解决这个问题提出建议吗?
答案 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