在Haskell中将复数Double与Double相乘

时间:2012-03-28 11:56:22

标签: haskell pattern-matching

当以下代码无法编译时,我有点意外:

-- Code 1
import Complex
type Velocity = Complex Double
type Force = Complex Double
type FrictionCoeff = Double

frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = mu * vel

错误说

Couldn't match expected type `Complex Double'
            with actual type `Double'
Expected type: Force
Actual type: FrictionCoeff
In the first argument of `(*)', namely `mu'
In the expression: mu * vel

所以,简而言之

-- Code 2
let z = 1 :+ 2
z * 3     -- Goes fine.
z * 2.5   -- Goes fine.
z * (2.5 :: Double)  -- Explodes.

复杂定义(*)为

instance  (RealFloat a) => Num (Complex a)  where
    (x:+y) * (x':+y') =  (x*x'-y*y') :+ (x*y'+y*x')

为什么3(Num a => a)和2.5(Fractional a => a)可以与(x:+ y)进行模式匹配,但Double不能?

2 个答案:

答案 0 :(得分:15)

首先,乘法运算符的类型是

(*) :: Num a => a -> a -> a

表示您只能将相同类型的数字相乘,这就是为什么将Complex Double乘以Double将无效。

那么为什么将复数与十进制文字相乘呢?

它的工作原理是因为数字文字在Haskell中是多态的,所以当你输入像42这样的整数文字时,它实际上意味着fromInteger 42。同样,像2.3这样的小数文字也会变成fromRational (23 % 10)。如果检查这些函数的类型,

fromInteger :: Num a => Integer -> a
fromRational :: Fractional a => Rational -> a

这意味着整数文字可以是任何数字类型,而十进制文字可以是任何小数类型。复数都是,这就是z * 3z * 2.5都可以工作的原因。

当你不处理文字时,你必须转换。例如,您可以通过写入来修复原始函数:

frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = (mu :+ 0) * vel

使用Hoogle可以轻松找到合适的转换函数,因为您可以按类型搜索函数。在这种情况下,搜索Double -> Complex Double会将(:+)作为最高结果。

答案 1 :(得分:7)

即使在“真实数学”中,也不能将实数与复数相乘;当您想要2 * (2 + 3i)时,您实际计算的是(2 + 0i) * (2 + 3i)。同样,在Haskell中,当你说:

let z = 1 :+ 2
z * 3

...然后3也被转换为Complex Double,使虚部为零。这只发生在文字数字(23.141等)上,因为Haskell的重载文字功能;由于文字没有默认类型(它们可以表示任何数字类型的值),Haskell可以说3在此上下文中具有类型Complex Double,并且自动调用适当的转换函数。 / p>

如果你想手动进行这种转换,也就是说,从一个变量的实数中得到一个复数,或者已经有一个不同的固定类型,你必须使用realToFrac函数,将任何实数转换为任何小数(在这种情况下,Complex计为小数。)

z * realToFrac (2.5 :: Double)

如果看起来比较适合您,您当然也可以手动附加:+ 0