定义向量时的哈斯克尔模棱两可事件 (+)



我想定义一个3D向量,x, y, z坐标。可以使用(+)运算符添加向量,并使用length函数计算长度

如果我想编译它,我收到以下错误:

It could refer to either `Prelude.+',
imported from `Prelude' at hello.hs:1:1
(and originally defined in `GHC.Num')
or `Main.+', defined at hello.hs:9:14

代码为:

data Vec3 = Vec3 {
x :: Float,
y :: Float,
z :: Float
} deriving (Eq,Show)
(+) :: Vec3 -> Vec3 -> Vec3
(Vec3 a b c) + (Vec3 t u w) = Vec3 (a+t) (b+u) (c+w)
length :: Vec3 -> Float
length (Vec3 a b c) = sqrt( a*a + b*b + c*c )
vv = Vec3 1.5 0.7 2.2
main :: IO ()
main = do
print $ length vv

要从字面上重载+运算符,您需要定义一个Num实例,例如

instance Num Vec3 where
Vec3 a b c + Vec3 t u w = Vec3 (a+t) (b+u) (c+w)
Vec3 a b c * Vec3 t u w = ...

事实上,这就是线性和hmatrix库对其向量类型所做的,(基本上)非常流行的NumPy Python框架也是如此。

我强烈建议不要这样做,因为向量空间的语义在某种意义上与普通标量数的语义不相容。特别是,您需要在此处定义乘法;正确使用这些类型签名的唯一方法是组件方面,就像 Matlab 的.*运算符一样。

Vec3 a b c * Vec3 t u w = Vec3 (a*t) (b*u) (c*w)

但这在数学上对向量本身没有意义,只对量在特定基础上的扩展有意义。此外,如果您可以错误地将向量定义为单个数字(linear将数字粘贴到所有向量分量中,哎呀!

更合适的是Monoid类,正如Reaktormonk所建议的那样。但是,您可能会发现自己也想要扩展操作

(*^) :: Float -> Vec3 -> Vec3
μ *^ Vec3 t u w = Vec3 (μ*t) (μ*u) (μ*w)

(与分量乘法不同,这是为任何向量空间定义的),Monoid不提供此功能。

这种类型的正确类是VectorSpace

instance AdditiveGroup Vec3 where
Vec3 a b c ^+^ Vec3 t u w = Vec3 (a+t) (b+u) (c+w)
negateV v = (-1)*^v
instance VectorSpace Vec3 where
type Scalar Vec3 = Float
μ *^ Vec3 t u w = Vec3 (μ*t) (μ*u) (μ*w)

答案很简单:您正在尝试重载 + 运算符,该运算符已在 Prelude 中定义。

Haskell在技术上不允许像大多数其他语言那样重载运算符,因此您不能只定义一个名为+的新函数,因为该函数已经用于将标量数相加。

相反,您可以尝试将其称为其他名称,例如addV,或者>+<或其他名称

我试着四处走动

import Prelude hiding ((+), length)

但是,您再也无法访问加法了。我建议在这里走Monoid路线。我会length重命名为vlength或类似名称,因为 IIRC 在任何类型类中都没有直接的概念。

import Data.Monoid
data Vec3 = Vec3 {
x :: Float,
y :: Float,
z :: Float
} deriving (Eq,Show)
instance Monoid Vec3 where
mappend (Vec3 a b c) (Vec3 t u w) = Vec3 (a+t) (b+u) (c+w)
mempty = Vec3 0 0 0
vlength :: Vec3 -> Float
vlength (Vec3 a b c) = sqrt( a*a + b*b + c*c )
vv = Vec3 1.5 0.7 2.2
v2 = Vec3 1.0 2.7 3.4
main :: IO ()
main = do
print $ vv <> v2
print $ vlength vv

最新更新