在Haskell中将复数Double与Double相乘

Nir*_*iel 7 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
Run Code Online (Sandbox Code Playgroud)

错误说

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
Run Code Online (Sandbox Code Playgroud)

所以,简而言之

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

复杂定义(*)为

instance  (RealFloat a) => Num (Complex a)  where
    (x:+y) * (x':+y') =  (x*x'-y*y') :+ (x*y'+y*x')
Run Code Online (Sandbox Code Playgroud)

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

ham*_*mar 15

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

(*) :: Num a => a -> a -> a
Run Code Online (Sandbox Code Playgroud)

这意味着你只能乘以相同类型的数字,这就是为什么乘以Complex Doublea Double将无效.

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

它的工作原理是因为数字文字在Haskell中是多态的,所以当你键入一个整数字面时42,它的确意味着fromInteger 42.同样,十进制文字就像2.3变成了fromRational (23 % 10).如果检查这些函数的类型,

fromInteger :: Num a => Integer -> a
fromRational :: Fractional a => Rational -> a
Run Code Online (Sandbox Code Playgroud)

这意味着整数文字可以是任何数字类型,而十进制文字可以是任何小数类型.复数都是,这就是为什么兼顾z * 3z * 2.5工作.

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

frictionForce :: FrictionCoeff -> Velocity -> Force
frictionForce mu vel = (mu :+ 0) * vel
Run Code Online (Sandbox Code Playgroud)

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


dfl*_*str 7

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

let z = 1 :+ 2
z * 3
Run Code Online (Sandbox Code Playgroud)

...然后将3转换为a Complex Double,使虚部成为零.这仅发生于文字数字(2,3.141因为Haskell的重载文字功能等); 由于文字没有默认类型(它们可以表示任何数字类型的值),Haskell可以说在此上下文中3有has类型Complex Double,并且自动调用适当的转换函数.

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

z * realToFrac (2.5 :: Double)
Run Code Online (Sandbox Code Playgroud)

:+ 0如果看起来更整洁,你当然也可以手动追加.

  • 我认为这不是真的.2 + 0i与2相同!在"真正的数学"中,实数是复数实数的一个子集,所以它才有效.它是Haskell,你可以表达这种关系,但你需要明确注入.基本上是因为我们在Haskell中没有子类型,所以我们用显式态射来表示包含. (4认同)