![](/img/trans.png)
[英]How to do recursion for recursive data type in function in Haskell
[英]how do you do recursion with type classes and data type in Haskell?
我正在研究一个类型 class,用于分数、向量和矩阵运算(即加法、减法、乘法),但不能完全获得向量实例,因为我不知道如何使用 function 的递归性质。
这是代码:
class MathObject a where
add :: a -> a -> a
sub :: a -> a -> a
mul :: a -> a -> a
type Vector = [Int]
data Vec = Vec Vector deriving (Show, Eq)
instance MathObject Vec where
add (Vec v1) (Vec v2) = addVecs (Vec v1) (Vec v2)
sub (Vec v1) (Vec v2) = subVecs (Vec v1) (Vec v2)
mul (Vec v1) (Vec v2) = mulVecs (Vec v1) (Vec v2)
addVecs :: Vec -> Vec -> [Int]
addVecs (Vec []) (Vec vec2)= (Vec [])
addVecs (Vec vec) (Vec []) = (Vec [])
addVecs (Vec (x:xs)) (Vec (y:ys)) = e1+e2:rest where
e1 = x
e2 = y
rest = addVecs (Vec xs) (Vec ys)
subVecs :: Vec -> Vec -> [Int]
subVecs (Vec []) (Vec v2) = (Vec [])
subVecs (Vec (x:xs)) (Vec (y:ys)) = x-y:rest where
rest = subVecs (Vec xs) (Vec ys)
mulVecs :: Vec -> Vec -> [Vec]
mulVecs (Vec []) (Vec vec2) = (Vec [])
mulVecs (Vec (x:xs)) (Vec (y:ys)) = e1 * e2:rest where
e1 = x
e2 = y
rest = mulVecs (Vec xs) (Vec ys)
我制作了分数实例并且它可以工作,所以我对类型类的工作原理有一些基本的了解,但我只是不知道如何处理递归类型。
我要说的是,首先,您将自己与太多不同的东西混淆了,这些东西的名字听起来像“矢量”。 为什么 Vec 类型有一个名为 Vec 的构造函数和 Vector 类型的字段? Vec 和 Vector 之间到底有什么区别? 如果Vector是[Int]的别名,那为什么有时候用普通的[Int]来表示向量呢? 您不包括您得到的编译器错误,但我可以清楚地看到,由于这些名称之间的类型不匹配,至少会出现其中一些错误。
所以要做的第一件事就是简化它:只有一个 Vector 类型,它是[Int]
newtype
新类型(或者data
,如果你还没有接触过新类型的话):
newtype Vector = Vector [Int]
现在您始终知道您使用的是 Vector 还是 [Int],并且可以使用 Vector 构造函数在两者之间进行转换。
我的下一个观察是addVecs
和朋友们显然有错误的类型:为什么他们接受两个Vector
输入并返回一个[Int]
? 他们应该坚持使用一种类型,最好是Vector
。 这是您的类型错误之一的原因: add
需要是a -> a -> a
类型,但您已将add
定义为addVecs
,它的类型是Vec -> Vec -> [Int]
- 那不是适合! 所以让我们改写
addVecs :: Vector -> Vector -> Vector
-- ...
现在,考虑到您为它选择的糟糕类型,实现是正确的。 但是为了更好的类型,我们必须在将a+b
转换为结果之前添加一个Vector
包装器。 不过,我不会展示该实现,因为有一个更好的实现。 不要自己进行递归,而是使用 Haskell 的众多灵活工具之一来处理列表: zipWith
。
addVecs (Vector v1) (Vector v2) = Vector $ zipWith (+) v1 v2
这与您的实现完全相同,但所有繁琐的包装和展开只完成一次,并且递归列表处理完全由zipWith
抽象掉。
您可以对subVecs
和mulVecs
做同样的事情,但您很快会注意到这里有一些重复。 最好将其提取到 function 中,以便这三个函数可以共享公共代码:
pointwise :: (Int -> Int -> Int) -> (Vector -> Vector -> Vector)
pointwise f (Vector v1) (Vector v2) = Vector $ zipWith f v1 v2
addVecs = pointwise (+)
subVecs = pointwise (-)
mulVecs = pointwise (*)
此时你甚至不需要addVecs
和 friends 的定义——你可以轻松地将它们内联到MathObject
实例中:
instance MathObject Vector where
add = pointwise (+)
sub = pointwise (-)
mul = pointwise (*)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.