2017-03-18 64 views
2

我想定义一个3D向量,其中x, y, z坐标。向量可以使用(+)运营商加入,并且可以使用length功能Haskell定义一个向量时模糊发生(+)

我碰到下面的错误来计算的长度,如果我想编译:

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 

回答

4

要逐字超载+操作,你需要定义一个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 = ... 

这实际上是什么linearhmatrix库为他们的载体类型做,因为做(基本上)也是非常流行的Python框架。

我强烈建议这个,因为矢量空间的语义在某种意义上与纯标量数字的语义不相容。特别是,你需要在这里定义乘法;能够正确对这些类型签名的工作的唯一方法是逐个分量,像Matlab的.*操作

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

,但并不能使数学感知到用于向量作为这样的,只为载体在特定的扩展依据。此外,它容易导致错误,如果你能错误地定义一个载体,一个数(在linear膏的数量为所有矢量分量,urgh!)

一个更适合的是Monoid类,如suggested by Reaktormonk。然而,你可能会发现自己也想缩放操作

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

(不像逐分量相乘,this is defined for any vector space)和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) 
3

答案很简单:你试图超载已经在Prelude中定义的+运算符。

Haskell没有技术上允许操作符重载大多数其他语言的方式做,所以你不能仅仅定义一个名为+,因为这个功能已经被用于添加标数字加在一起的新功能。

相反,你可以尝试调用别的东西,像addV,或者也许>+<什么

3

我试着绕来绕去的

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 
+1

+1对于_not_不推荐'Num'实例,但是'Monoid'也不是那么棒的IMO,因为它不允许标量乘法。 – leftaroundabout