在Haskell中合并自定义数据类型的记录
我有一些数据类型在Haskell中合并自定义数据类型的记录,haskell,Haskell,我有一些数据类型 data SomeType = SomeType { a :: Maybe Int , b :: Maybe String , c :: Maybe OtherType } 和两个类型的变量 st1 = SomeType (Just 1) (Just "hello") Nothing st2 = SomeType Nothing (Just "world") Nothing
data SomeType = SomeType { a :: Maybe Int
, b :: Maybe String
, c :: Maybe OtherType }
和两个类型的变量
st1 = SomeType (Just 1) (Just "hello") Nothing
st2 = SomeType Nothing (Just "world") Nothing
我将如何合并它们,并按第二个优先级排序
merged = SomeType (Just 1) (Just "world") Nothing
这里的st2
是Nothing
,因此首选st1
中的仅1
。对于
b
,只需来自st2
的“世界”
即可覆盖st1
的只需“你好”
我的简单方法是
merge :: SomeType -> SomeType -> SomeType
merge (SomeType a1 b1 c1) (SomeType a2 b2 c2) =
SomeType { a = maybe a1 pure a2
, b = maybe b1 pure b2
, c = maybe c1 pure c2 }
实际的类型比这个示例要大,可能其他类型也需要递归合并
编辑:
另外,我知道记录字段更新的形式如下
st1 { b = Just "world" }
创建具有更新字段的新记录。
不确定这对我的情况是否有帮助。对于简单的情况,您可以使用
orElse
如果需要合并两个对象中的数据,则可以创建辅助对象
mergeHelper :: (a -> a-> a) -> Maybe a -> Maybe a -> Maybe a
mergeHelper _ None x = x
mergeHelper _ (Maybe x) _ = Maybe x
mergeHelper f (Maybe x) (Maybe y) = Maybe $ f x y
类型为
SomeType->SomeType->SomeType
的函数看起来像是半群
的候选函数,或者至少是可以用半群
实现的函数。有两种选择
显式合并
如果将SomeType
保持在OP中,则可以编写如下显式的merge
函数:
merge :: SomeType -> SomeType -> SomeType
merge x y = toSomeType $ toTriple x <> toTriple y
where
toTriple (SomeType a b c) = (Last <$> a, Last <$> b, c)
toSomeType (a, b, c) = SomeType (getLast <$> a) (getLast <$> b) c
请注意,这些字段不仅仅是Maybe
值,而是显式的Maybe Last
值。这就产生了一个明确的半群
实例:
instance Semigroup OtherType where
(OtherType foo1 bar1) <> (OtherType foo2 bar2) =
OtherType (foo1 <> foo2) (bar1 <> bar2)
(我在GHCi会话中添加了一些换行符,以使其更具可读性…)
独异点
这些类型也可以是Monoid
实例:
instance Monoid OtherType where
mempty = OtherType Nothing Nothing
这可能是有用的,所以您不妨考虑添加这些实例……
,可以使用<代码>替代< /代码>实例,用于<代码>可能类型。import Control.Applicative -- for <|>
merge (SomeType a1 b1 c1) (SomeType a2 b2 c2)
= SomeType (a2 <|> a1) (b2 <|> b1) (c2 <|> c1)
你的方法有什么问题?这似乎是可行的。我在那里使用的简单方法似乎有点太乏味了,我不知道如何处理嵌套的自定义类型。
Maybe(Last…
不是有点多余吗?为什么不仅仅是Last…
?@Carl上面使用的Last
类型是来自Data.Semigroup
,而不是来自Data.Monoid
的类型。虽然声明您可以使用选项(Last a)从Data.Monoid获取Last的行为,但是选项
包装器将被弃用,因为半组
现在是Monoid
的超类。所以,也许(最后一个a)
似乎是持久的抽象……谢谢你的回答。这非常有效,并向我介绍了更多的Haskell概念。不过还有一件事,如果我有一个具有许多属性的大型数据类型,那么定义
会变得相当长。有什么简写吗?@n.z.如果您定义类型以便它们具有明确的半群
实例,您可能可以使用GHC扩展名,如DeriveAnyClass
,但我不确定。您也可以使用Generic
类型类或Template Haskell使某些内容工作,但这些都不是我的专业领域,因此我可能对这些建议有错误…c1和c2是可能是其他类型的,因此合并c1 c2
将无法工作。需要类似于实例(可合并a)=>Mergeable(可能a)where的东西;合并一个空=一个空;不合并b=b;合并a b=合并a b
Edit:在注释oops中为无代码块设置格式<代码>合并c1 c2
就足够了。
instance Monoid OtherType where
mempty = OtherType Nothing Nothing
import Control.Applicative -- for <|>
merge (SomeType a1 b1 c1) (SomeType a2 b2 c2)
= SomeType (a2 <|> a1) (b2 <|> b1) (c2 <|> c1)
class Mergeable a where
merge a1 a2 :: a -> a -> a
instance Mergeable SomeType where
merge (SomeType a1 b1 c1) (SomeType a2 b2 c2)
= SomeType (a2 <|> a1) (b2 <|> b1) (merge <$> c1 <*> c2) -- not merge c2 c1
instance Mergeable OtherType where
merge (OtherType a1 b1) (OtherType a2 b2) = ...