如何在数据类型上编写表达式并在haskell中打印出来? [英] How to write an expression over a data type and print it out in haskell?
问题描述
我必须写一个表达式来构造多项式x ^ 3 + x + 1的Poly表示形式.
I am having to write an expression to construct a Poly representation of the polynomial x^3 + x + 1.
我写的代数数据类型Poly是:
My algebraic data type Poly, which I wrote is:
data Poly = Lit Integer |
Var |
Add Poly Poly |
Mul Poly Poly
我能想到的表达式是这个,但是如何使用print()打印出结果呢?:
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)
允许传递Poly表达式,然后将其转换为String.但是,以上工作告诉我,我没有(显示Poly)的实例,我不确定这是什么意思.
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.
推荐答案
首先, expr
的类型错误. expr :: Poly
是正确的.您也可以询问GHCi有关该类型的信息:
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
为了使用 print
或类似的功能,类型必须是 Show
的实例,因为
In order to use print
or a similar function, the type needs to be an instance of Show
, since
print :: Show a => a -> IO ()
一种实现此目的的方法是简单地自动派生 Show
实例:
One way to achieve this is to simply derive the Show
instance automatically:
data Poly = .... deriving (Show)
但是,这不会导致您期望的结果"x ^ 3 + x + 1"
.您需要自己编写 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.
解决此问题的另一种方法是实现 Show
类型类的 showsPrec
方法.该函数通过一个优先级进行穿线,以便您可以选择何时应用括号,从而使您可以打印出更像 1 + x + x * x * x 的表达式,而不是使用<上面的代码>显示
.虽然 showsPrec
的类型有点怪异,但它看起来像
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
哪里
type ShowS = String -> String
ShowS
类型只是编码一个差异列表,它允许通过函数组合而不是简单的串联来更有效地构造String.对于这种 Poly
类型,您可能想要实现以下方式:
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
d
参数表示优先级.每次调用时,我们都会检查优先级是否大于每个运算符的优先级,如果是,它将在该表达式周围添加括号( showParen
有条件地在 ShowS
周围放置括号),然后它以正确的优先级构建表达式的左右树. 6
和 7
的优先级来自询问GHCi :i(+)
和:i(*)
,分别显示每个运算符的固定性(优先级).
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
请记住,由于 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))
并进行测试:
> :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.
这篇关于如何在数据类型上编写表达式并在haskell中打印出来?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!