簡體   English   中英

遍歷Haskell中的自定義數據類型

[英]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這樣的類型,您真正想要的是諸如diagramsTransformable類之類的東西。 但是我不認為有任何自動方法可以為此派生實例。 實際上,由於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 ff轉換為適用於任何類型的函數:如果mkT f的參數為Float ,則應用f ,否則按原樣返回該參數。 新構造的通用函數稱為“變換”: mkTT代表該變換。

然后, gmapT將變換mkT f應用於圓的所有字段。 請注意,這是一個包含(Float, Bool)的字段,該字段將不受影響。 everywhere使用而不是gmapT遞歸更深。

請注意,我不是這種方法的忠實擁護者。 如果出於任何原因更改了字段的類型,那么該更改將不會觸發類型錯誤,但是gmapT (mkT ...)現在將直接跳過該字段。

泛型編程可能很方便,但有時會有點過多,因為在運行時類型錯誤可以悄無聲息地轉換成意外的結果。 小心使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM