如何在数据类型上写表达式并在haskell中打印出来?

时间:2014-10-27 08:19:13

标签: haskell

我必须编写一个表达式来构造多项式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)的实例,我不确定它是什么意思。

1 个答案:

答案 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周围),然后构建左右表达式的树具有正确的优先级。 67的优先级来自于询问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))

这与我们在源代码中定义它的方式相同。