简体   繁体   English

遍历Haskell中的自定义数据类型

[英]Iterating over custom data types in Haskell

I have a custom data type that looks like this: 我有一个自定义数据类型,如下所示:

data Circle = Circle
        { radius                   :: Float
        , xPosition                :: Float
        , yPosition                :: Float
        }

I want to be able to write a scale function that can take a given circle and change its size like this: 我希望能够编写一个缩放函数,该函数可以采用给定的圆并更改其大小,如下所示:

aCircle = Circle 1.5 1 1
scaleFn aCircle 10

The desired output for this example with scale of 10 would be: 对于此示例,比例为10的期望输出为:

Circle 15 10 10

How can I create a function where I can iterate over each field and multiple the values by a constant? 如何创建一个函数,可以在每个字段上进行迭代并将值乘以一个常数? In my actual use case I need a way to map over all the fields as there are many of them. 在我的实际用例中,我需要一种映射所有字段的方法,因为它们很多。

Scaling by a factor is generally a vector space operation . 按因子进行缩放通常是向量空间操作 You could do the following: 您可以执行以下操作:

{-# 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

(result: Circle {radius = 15.0, xPosition = 10.0, yPosition = 10.0} ). (结果: Circle {radius = 15.0, xPosition = 10.0, yPosition = 10.0} )。

(requires vector-space >= 0.11 , which has just added support for generic-derived instances .) (需要vector-space >= 0.11 ,它刚刚添加了对泛型实例的支持 。)

However I should remark that Circle as such is not really a good VectorSpace instance: adding two circles doesn't make any sense, and scaling by a negative factor gives a bogus radius. 但是,我应该指出, Circle本身并不是一个很好的VectorSpace实例:添加两个圆没有任何意义,而按负数进行缩放会导致虚假半径。 Only define such an instance if your real use case follows the actual vector space axioms . 仅当您的实际用例遵循实际的向量空间公理时,才定义此类实例。

What you really want for a type like Circle is something like diagrams ' Transformable class . 对于诸如Circle这样的类型,您真正想要的是诸如diagramsTransformable类之类的东西。 But I don't think there's any automatic way to derive an instance for that. 但是我不认为有任何自动方法可以为此派生实例。 In fact, since diagrams has – unfortunately IMO – switched from vector-space to linear , something like this has become considerably tougher to do even in principle. 实际上,由于diagrams (不幸的是IMO)已从vector-space切换为linear ,因此即使从原则上讲,这样的操作也变得相当困难。

You can use "scrap your boilerplate": 您可以使用“取消样板”:

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)

Intuitively, above, mkT f transforms f into a function which is applicable to any type: if the argument of mkT f is a Float , then f is applied, otherwise the argument is returned as it is. 直观上,上面的mkT ff转换为适用于任何类型的函数:如果mkT f的参数为Float ,则应用f ,否则按原样返回该参数。 The newly constructed general function is called a "transformation": the T in mkT stands for that. 新构造的通用函数称为“变换”: mkTT代表该变换。

Then, gmapT applies the transformation mkT f to all the fields of the circle. 然后, gmapT将变换mkT f应用于圆的所有字段。 Note that is a field contained, say, (Float, Bool) that float would be unaffected. 请注意,这是一个包含(Float, Bool)的字段,该字段将不受影响。 Use everywhere instead of gmapT to recursively go deeper. everywhere使用而不是gmapT递归更深。

Note that I'm not a big fan of this approach. 请注意,我不是这种方法的忠实拥护者。 If for any reason you change the type of a field, that change will not trigger a type error but gmapT (mkT ...) will now simply skip over that field. 如果出于任何原因更改了字段的类型,那么该更改将不会触发类型错误,但是gmapT (mkT ...)现在将直接跳过该字段。

Generic programming can be convenient, but sometimes a bit too much, in that type errors can be silently transformed into unexpected results at runtime. 泛型编程可能很方便,但有时会有点过多,因为在运行时类型错误可以悄无声息地转换成意外的结果。 Use with care. 小心使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM