如何做(即推导)有理数和新类型的乘法(使用*)

How to do (i.e. derive) multiplication (using *) of a Rational and a new type

我正在实现一个小程序来处理多项式运算,灵感来自 upenn 的一项作业 cis194。代码如下(我省略了一些不相关的代码):

newtype Poly = Poly [Rational]
    deriving Eq
s :: Poly
s = Poly [1, 0]

instance Num Poly where
    (Poly a) + (Poly b) = ...
    (Poly a) * (Poly b) = ...
    negate (Poly xs) = ...
    fromInteger i = Poly [fromInteger i]

instance Fractional Poly where
    fromRational f = Poly [fromRational f]

instance Show Poly where
    show (Poly a) = ...

pdiv :: Poly -> Poly -> (Poly, Poly)
pdiv (Poly a) (Poly b) = ...

cauer1 :: Poly -> Poly -> String
cauer1 a b =
    let (q, r) = pdiv a b
    in if r == 0 then (show q) else
        (show q) ++ " + 1/(" ++ (cauer1 b r) ++ ")"

此代码有效。例如,我可以获得多项式的长除法结果,以便使用 cauer1 函数制作 Cauer 1 形式的电路。 (电路分析)

ghci > let a = Poly [1, 23%6, 10%3, 0] -- represents: s^3 + 23/6 s^2 + 10/3 s
ghci > let b = Poly [1, 3, 2]
ghci > cauer1 a b
"s + 1/(6%5 + 1/(25%42s + 1/(49%5 + 1/(1%14s))))"

我现在想要的是做这样的事情:

ghci > cauer1 (s^3 + 23%6*s^2 + 10%3*s) (s^2 + 3*s + 2)

得到相同的结果。

起初我的 Poly 类型定义为 Poly [Double],在创建 NumFractionalPoly 实例并实现 fromInteger 之后和 fromRational,我可以做类似 s * 1s * 2.33.4 * s^2 的事情,然后强大的类型类将毫无问题地将它们计算为 Poly。但是这样一来结果全是小数,精度不高,所以改成Poly [Rational]。但是现在我做不到 3%2 * s,他们只是根据 ghc:

输入不匹配
<interactive>:1:9:
Couldn't match expected type ‘Ratio a’ with actual type ‘Poly’
In the second argument of ‘(*)’, namely ‘s’
In the expression: (3 % 2) * s

我发现 RationalRatio IntegerRatioNumFractional 的实例,为什么这不起作用,怎么能我修好了吗?

I can do something like s * 1, s * 2.3, 3.4 * s^2 ...

是的,因为(引自 Prelude 文档):

A floating literal stands for an application of fromRational to a value of type Rational, so such literals have type (Fractional a) => a.

.. 这意味着你可以在任何可以使用 Poly (作为 Fractional 的子类)

的地方使用这样的文字

这并不适用于例如3%2,它不是文字,而是类型为 Integral a => Ratio a 的表达式,这只意味着我可以在任何地方使用这样的表达式我可以使用 Ratio a(前提是 a Integral)。这远没有那么灵活。

然后,您需要的是一个运算符 %,这样 3%2 将获得类型 (Fractional a) => a,就像文字 3.14

这可以通过重新定义它来完成,如下例所示

import qualified Data.Ratio as R

(%) :: Fractional a => Integer -> Integer -> a
x % y = fromRational ((R.%) x y)

-- the rest as in your example

现在你可以

*YourExample> :t 3%2 * s -- ask ghci whether this makes sense ...
3%2 * s :: Poly          -- yes, it does!