2017-03-18 55 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