问题
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 undefined
s, 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