我正在尝试实现一个乘以多项式的函数(用列表表示 - 3x ^ 2 + 5x + 2 = P [2,5,3]):
newtype Poly a = P [a]
plus :: Num a => Poly a -> Poly a -> Poly a
plus (P a) (P b) = P (map (\(y,z) -> z + y) (zipWithPadding 0 a b))
where
zipWithPadding :: (Num a) => a -> [a] -> [a] -> [(a, a)]
zipWithPadding e (aa: as) (bb: bs) = ((aa, bb): zipWithPadding e as bs)
zipWithPadding e [] bs = zip (repeat e) bs
zipWithPadding e as [] = zip as (repeat e)
times :: Num a => Poly a -> Poly a -> Poly a
times (P a) (P b) = sum $ multList 0 [] a b
where
multList :: Num a => Int -> [Poly a] -> [a] -> [a] -> [Poly a]
multList _ s [] _ = s
multList e s (aa:as) bs = multList (e + 1) (s ++ (multElement e aa bs)) as bs
multElement :: Num a => Int -> a -> [a] -> [Poly a]
multElement e aa bs = [P $ replicate e 0 ++ (map (*aa) bs)]
instance Num a => Num (Poly a) where
(+) = plus
(*) = times
negate = undefined
fromInteger = undefined
-- No meaningful definitions exist
abs = undefined
signum = undefined
但是当我尝试运行时,我收到undefined
错误:
*HW04> times (P [1,2,2]) (P [1,2])
*** Exception: Prelude.undefined
我很困惑。
答案 0 :(得分:4)
显然,您正在调用Num实例中的undefined
方法之一。
您可以使用以下定义确定调用哪一个:
negate = error "Poly negate undefined"
fromInteger = error "Poly fromInteger undefined"
abs = error "Poly abs undefined"
signum = error "Poly signum undefined"
运行测试表达式得到:
Poly *** Exception: Poly fromInteger undefined
问题在于您使用sum
,其实质上定义为:
sum xs = foldl (+) 0 xs
因此正在呼叫fromInteger 0
。您可以通过以下方式解决此问题:
fromInteger x = P [ fromInteger x ]
<强>更新强>
fromInteger
Poly a
需要以这种方式定义的原因是
因为我们需要构建Num a
值列表,fromInteger x
是从整数值Num a
创建x
的方法。
答案 1 :(得分:2)
虽然存在环单态Num a => a -> Poly a
,但多项式实际上不是Num。
弃掉Num实例并使用foldl plus
代替sum
。
答案 2 :(得分:0)
我将采取的立场是,您不应该仅仅为了劫持类的功能而定义类的实例。 Num
实例的最小定义需要定义某些函数;明确地将undefined
分配给这些名称不符合定义。考虑Haskell为列表连接提供特定的运算符(++)
,而不是简单地使用像
(+)
instance Num [a] where
a + [] = a
[] + b = b
(a:as) + b = a:(as + b)
(*) = undefined
negate = undefined
-- etc
相反,定义一个 提供所需操作的类。在这种情况下,您需要Ring
,这是一种类型,以及遵循某些定律的两个操作,加法和乘法。 (简单地说,操作就像你想象的那样整数作为一个例子,除了乘法不需要是可交换的。)
在Haskell中,我们将类定义为
class Ring a where
rplus :: a -> a -> a -- addition
rmult :: a -> a -> a -- multiplication
rnegate :: a -> a -- negation
runit :: a -- multiplicative identity
rzero :: a -- additive identity, multiplicative zero
具有有效Num
实例的任何值都会形成一个响铃,但您需要单独定义实例。
instance Ring Integer where
rplus = (+)
rmult = (*)
rnegate = negate
rzero = 0
runit = 1
instance Ring Float
rplus = (+)
rmult = (*)
rnegate = negate
rzero = 0
runit = 1
-- etc
您可以为多项式定义Ring
的实例,只要系数也形成一个环。
newtype Poly a = P [a]
instance Ring a => Ring (Poly a) where
-- Take care to handle polynomials with different degree
-- Note the use of rplus and rzero instead of (+) and 0
-- when dealing with coefficients
rplus (P a) (P b) = case (compare (length a) (length b)) of
LT -> rplus (P (rzero:a)) (P b)
EQ -> P $ zipWith rplus a b
GT -> rplus (P a) (P (rzero:b))
-- I leave a correct implementation of rmult as an exercise
-- for the reader.
rmult = ...
rnegate (P coeffs) = P $ map rnegate coeffs
rzero = P [0]
runit = P [1]