如何在数据类型上编写表达式并在haskell中打印出来? [英] How to write an expression over a data type and print it out in haskell?

查看:42
本文介绍了如何在数据类型上编写表达式并在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 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))

并进行测试:

> :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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆