我想定义一个带有x, y, z
坐标的3D矢量。可以使用(+)
运算符添加向量,并且可以使用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
答案 0 :(得分: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 = ...
这实际上是linear和hmatrix库为其矢量类型所做的事情,(基本上)也是非常受欢迎的NumPy Python框架。
我强烈建议反对这个,因为向量空间的语义在某种意义上与普通标量数字的语义不兼容。特别是,你需要在这里定义乘法;正确使用这些类型签名的唯一方法是组件方式,就像使用Matlab的.*
运算符
Vec3 a b c * Vec3 t u w = Vec3 (a*t) (b*u) (c*w)
但这并不能在数学上对矢量本身有意义,只是为了在特定基础上扩展矢量 。如果你可以错误地将一个向量定义为单个数字(在linear
中将数字粘贴到所有向量组件中,那么它很容易导致错误!呃!)
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)
答案 1 :(得分:3)
答案很简单:你试图重载已经在Prelude中定义的+运算符。
Haskell技术上 技术上允许运算符重载大多数其他语言的方式,所以你不能只定义一个名为+
的新函数,因为这个函数已经是用于将标量数字加在一起。
相反,您可以尝试将其称为其他内容,例如addV
,或者>+<
或其他
答案 2 :(得分: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