How to write an expression over a data type and print it out in haskell?

浪子不回头ぞ 提交于 2021-01-28 07:54:47

问题


I am having to write an expression to construct a Poly representation of the polynomial x^3 + x + 1.

My algebraic data type Poly, which I wrote is:

data Poly = Lit Integer |
            Var |
            Add Poly Poly |
            Mul Poly Poly

The expression I can think of is this, but how can I be able to print out the result using print()?:

expr::Poly->Poly
expr = Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var

Also, I'd like to write a function like this:

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)

to enable the passing of a Poly expression and then converting it to a String. However, the above work tells me that I have no instance for (show Poly), which I am not sure what it mean.


回答1:


First of all, expr has the wrong type. expr :: Poly would be correct. You could also ask GHCi about that type:

> :t Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var
Add (Lit 1) $ Add (Var) $ Mul (Var) $ Mul Var Var :: Poly

In order to use print or a similar function, the type needs to be an instance of Show, since

print :: Show a => a -> IO ()

One way to achieve this is to simply derive the Show instance automatically:

data Poly = .... deriving (Show)

However, this won't lead to your desired result "x^3 + x + 1". You need to write the Show instance yourself, for example:

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"

Note that this still isn't what you're looking for:

> show expr
"(1) + ((x) + ((x) * ((x) * (x))))"

However, that information should enable you to create your own instance.


Another way to solve this problem is to implement the showsPrec method of the Show typeclass. This function threads through a precedence so that you can choose when to apply parentheses, allowing you to print out an expression that looks more like 1 + x + x * x * x instead of the simple example using show above. The type of showsPrec is a little wonky, though, it looks like

showsPrec :: Show a => Int -> a -> ShowS

Where

type ShowS = String -> String

The ShowS type is just encoding a diff list, which allows more efficient construction of Strings through function composition rather than simple concatenation. For this Poly type, you would want to implement it something like this:

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

The d parameter represents the precedence. Each call we check if the precedence is greater than that of each operator, and if so it adds parentheses around that expression (showParen conditionally puts parentheses around a ShowS), and then it builds the left and right trees of the expression with the correct precedence. The precedences of 6 and 7 come from asking GHCi :i (+) and :i (*), which show the fixity (precedence) of each operator respectively.

With another instance we can use this to write very readable instances as well:

instance Num Poly where
    (+) = Add
    (*) = Mul
    negate = Mul (Lit (-1))
    fromInteger = Lit
    abs = undefined
    signum = undefined

Keep in mind that this isn't entirely well behaved due to the undefineds, but it allows us to write code like

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))

And to test:

> :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))

Which is identical to how we defined it in source code.



来源:https://stackoverflow.com/questions/26583420/how-to-write-an-expression-over-a-data-type-and-print-it-out-in-haskell

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!