[英]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.