我必须编写一个表达式来构造多项式x ^ 3 + x + 1的Poly表示。
我的代数数据类型Poly,我写的是:
data Poly = Lit Integer |
Var |
Add Poly Poly |
Mul Poly Poly
我能想到的表达方式是这样,但我怎么能用print()打印出结果?:
expr::Poly->Poly
expr = Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var
另外,我想写一个这样的函数:
showPoly::Poly->String
showPoly (Lit x) = show x
showPoly (Var) = "x"
showPoly (Add x y) = (show x) ++ " + " ++ (show y)
showPoly (Mul x y) = (show x) ++ "*" ++ (show y)
启用Poly表达式的传递,然后将其转换为String。但是,上面的工作告诉我,我没有(show Poly)的实例,我不确定它是什么意思。
答案 0 :(得分:3)
首先,expr
的类型错误。 expr :: Poly
是正确的。你也可以向GHCi询问这种类型:
> :t Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var :: Poly
为了使用print
或类似的函数,类型必须是Show
的实例,因为
print :: Show a => a -> IO ()
实现此目的的一种方法是简单地自动派生 Show
实例:
data Poly = .... deriving (Show)
然而,这不会导致您想要的结果"x^3 + x + 1"
。您需要自己编写Show
实例,例如:
instance Show Poly where
show (Add x y) = "(" ++ show x ++ ") * (" ++ show y ++ ")
show (Mul x y) = "(" ++ show x ++ ") * (" ++ show y ++ ")
show (Lit x) = show x
show (Var) = "x"
请注意,这仍然不是您正在寻找的内容:
> show expr "(1) + ((x) + ((x) * ((x) * (x))))"
但是,该信息应该可以让您创建自己的实例。
解决此问题的另一种方法是实现showsPrec
类型类的Show
方法。此函数通过优先级进行线程化,以便您可以选择何时应用括号,从而允许您打印出更像1 + x + x * x * x
的表达式,而不是使用上面的show
打印的简单示例。 showsPrec
的类型有点不稳定,但看起来像
showsPrec :: Show a => Int -> a -> ShowS
其中
type ShowS = String -> String
ShowS
类型只是编码差异列表,它允许通过函数组合而不是简单的连接更有效地构造字符串。对于此Poly
类型,您可能希望实现如下:
instance Show Poly where
showsPrec d poly = case poly of
Lit i -> shows i
Var -> showString "x"
Add l r -> showParen (d > add_prec)
$ showsPrec add_prec l
. showString " + "
. showsPrec add_prec r
Mul l r -> showParen (d > mul_prec)
$ showsPrec mul_prec l
. showString " * "
. showsPrec mul_prec r
where
-- infixl 6 +
add_prec = 6
-- infixl 7 *
mul_prec = 7
d
参数表示优先级。每次调用我们检查优先级是否大于每个运算符的优先级,如果是,则在该表达式周围添加括号(showParen
有条件地将括号放在ShowS
周围),然后构建左右表达式的树具有正确的优先级。 6
和7
的优先级来自于询问GHCi :i (+)
和:i (*)
,它们分别显示了每个运算符的固定性(优先级)。
使用另一个实例,我们也可以使用它来编写非常易读的实例:
instance Num Poly where
(+) = Add
(*) = Mul
negate = Mul (Lit (-1))
fromInteger = Lit
abs = undefined
signum = undefined
请注意,由于undefined
s,这并不完全正常,但它允许我们编写类似
x :: Poly
x = Var
expr1 :: Poly
expr1 = 1 + x + x * x * x
expr2 :: Poly
expr2 = 2 * (x + 3 * x)
expr3 :: Poly
expr3 = (4 + x) * (4 * (x + 2) * x + x * (x + x + 4))
并测试:
> :l poly.hs
> expr1
1 + x + x * x * x
> expr2
2 * (x + 3 * x)
> expr3
(4 * x) * (4 * (x + 2) * x + x * (x + x + 4))
这与我们在源代码中定义它的方式相同。