我想定义一个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