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