[英]Iterating over custom data types in Haskell
我有一個自定義數據類型,如下所示:
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
}
我希望能夠編寫一個縮放函數,該函數可以采用給定的圓並更改其大小,如下所示:
aCircle = Circle 1.5 1 1
scaleFn aCircle 10
對於此示例,比例為10的期望輸出為:
Circle 15 10 10
如何創建一個函數,可以在每個字段上進行迭代並將值乘以一個常數? 在我的實際用例中,我需要一種映射所有字段的方法,因為它們很多。
按因子進行縮放通常是向量空間操作 。 您可以執行以下操作:
{-# LANGUAGE TypeFamilies, DeriveGeneric #-}
import Data.VectorSpace
import GHC.Generics (Generic)
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
} deriving (Generic, Show)
instance AdditiveGroup Circle
instance VectorSpace Circle where
type Scalar Circle = Float
main = print $ Circle 1.5 1 1 ^* 10
(結果: Circle {radius = 15.0, xPosition = 10.0, yPosition = 10.0}
)。
(需要vector-space >= 0.11
,它剛剛添加了對泛型實例的支持 。)
但是,我應該指出, Circle
本身並不是一個很好的VectorSpace
實例:添加兩個圓沒有任何意義,而按負數進行縮放會導致虛假半徑。 僅當您的實際用例遵循實際的向量空間公理時,才定義此類實例。
對於諸如Circle
這樣的類型,您真正想要的是諸如diagrams
的Transformable
類之類的東西。 但是我不認為有任何自動方法可以為此派生實例。 實際上,由於diagrams
(不幸的是IMO)已從vector-space
切換為linear
,因此即使從原則上講,這樣的操作也變得相當困難。
您可以使用“取消樣板”:
import Data.Generics
data Circle = Circle
{ radius :: Float
, xPosition :: Float
, yPosition :: Float
}
deriving (Show, Data)
circleModify :: (Float -> Float) -> Circle -> Circle
circleModify f = gmapT (mkT f)
直觀上,上面的mkT f
將f
轉換為適用於任何類型的函數:如果mkT f
的參數為Float
,則應用f
,否則按原樣返回該參數。 新構造的通用函數稱為“變換”: mkT
的T
代表該變換。
然后, gmapT
將變換mkT f
應用於圓的所有字段。 請注意,這是一個包含(Float, Bool)
的字段,該字段將不受影響。 everywhere
使用而不是gmapT
遞歸更深。
請注意,我不是這種方法的忠實擁護者。 如果出於任何原因更改了字段的類型,那么該更改將不會觸發類型錯誤,但是gmapT (mkT ...)
現在將直接跳過該字段。
泛型編程可能很方便,但有時會有點過多,因為在運行時類型錯誤可以悄無聲息地轉換成意外的結果。 小心使用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.