简体   繁体   English

在Haskell中通过构造函数对数据类型进行分组

[英]Grouping data types by constructor in Haskell

Given this data type 给定此数据类型

data Val = X Int | Y Bool | Z Double deriving (Eq, Show)

and a list such as 以及诸如的列表

let vals = [X 1, Z 2.7, Y True, X 2, Z 3.14, Y True]

how to group elements in vals into this list, 如何将vals元素分组到这个列表中,

[[X 1,X 2],[Y True,Y True],[Z 2.7, Z 3.14]]

To add to @RamonSnir's answer, the function for grouping a data type by constructors can be also constructed automatically using the "Scrap your boilerplate" framework: 要添加到@ RamonSnir的答案,还可以使用“Scrap the boilerplate”框架自动构建用于按构造函数对数据类型进行分组的函数:

{-# LANGUAGE DeriveDataTypeable #-}

import Data.Data
import Data.Function (on)
import Data.List (groupBy, sort)

data Val = X Int | Y Bool | Z Double
  deriving (Eq, Ord, Show, Typeable, Data)

vals :: [Val]
vals = [X 1, Z 2.7, Y True, X 2, Z 3.14, Y True]

main :: IO ()
main = print $ groupBy (on (==) toConstr) $ sort vals

The two important parts are: 两个重要部分是:

  • derive Typeable and Data , and 派生可TypeableData ,和
  • use toConstr to get the representation of the constructor used in a particular value. 使用toConstr来获取特定值中使用的构造函数的表示。

I've the following: 我有以下几点:

data Val = X Int | Y Bool | Z Double deriving (Eq, Ord, Show)

vals :: [Val]
vals = [X 1, Z 2.7, Y True, X 2, Z 3.14, Y True]

valCtorEq :: Val -> Val -> Bool
valCtorEq (X _) (X _) = True
valCtorEq (Y _) (Y _) = True
valCtorEq (Z _) (Z _) = True
valCtorEq _ _ = False

And then: 然后:

*Main Data.List> groupBy valCtorEq $ sort vals
[[X 1,X 2],[Y True,Y True],[Z 2.7,Z 3.14]]

(This is probably extreme overkill, but it was a fun question to tinker around with!) (这可能是极端矫枉过正,但修补它是一个有趣的问题!)

The provided answers suffer from 3 slight problems: 提供的答案有3个小问题:

  • What if the type under consideration isn't in Ord (because for example, there's a function in there somewhere)? 如果所考虑的类型不在Ord (例如,某处有某个函数)会怎么样?
  • Also, should this operation be O(n log n) in the length of the list? 此外,该操作是否应该在列表的长度为O(n log n)?
  • Finally, the example provided isn't sufficient to determine whether the grouping should be stable, that is: should the result of grouping [X 2, X 1] be [X 1, X 2] (that's what you get if you use the sort -based solutions) or should the elements be kept in their original order? 最后,提供的示例不足以确定分组是否应该是稳定的,即:分组[X 2, X 1]的结果应该是[X 1, X 2] (如果您使用的话,那就是你得到的结果)基于sort的解决方案)或元素应保持原始顺序?

So here is the most general solution I could come up with. 所以这是我能提出的最通用的解决方案。 It's stable, it doesn't need Ord (in fact you don't even need to touch the original datatype) and it runs in about O(n * min(n,W)) time where W is the word size of your machine (on mine, it's 64). 它是稳定的,它不需要Ord (事实上​​你甚至不需要触摸原始数据类型)并且它运行在大约O(n * min(n,W))时间,其中W是机器的字大小(在我的身上,它是64)。 That is, it's linear once the list gets longer than 64-ish elements (I say 'about', because the grouped elements still need to be reconstituted from the difference lists). 也就是说,一旦列表比64-ish元素更长,它就是线性的(我说'约',因为分组的元素仍然需要从差异列表重构)。

{-# LANGUAGE DeriveDataTypeable, StandaloneDeriving #-} 

import Data.Data
import qualified Data.IntMap as IM

groupByConstructor :: Data a => [a] -> [[a]]
groupByConstructor = map ($ []) . IM.elems . IM.fromListWith (flip (.)) 
    . map (\a -> (constrIndexOf a, (a:))) where constrIndexOf = constrIndex . toConstr

-- definition of Val as originally posed, without Ord:
data Val = X Int | Y Bool | Z Double deriving (Eq, Show)

deriving instance Typeable Val
deriving instance Data Val

-- new example:
vals = [X 2, Z 2.7, Y True, X 1, Z 3.14, Y False, Z 0.2]

and now groupByConstructor vals gives [[X 2, X 1],[Y True, Y False],[Z 2.7, Z 3.14, Z 0.2]] as I think it should. 现在groupByConstructor vals给出了[[X 2, X 1],[Y True, Y False],[Z 2.7, Z 3.14, Z 0.2]] ,我认为应该如此。


It doesn't work for sorting lists of Int s, Char s, Float s, or non-representable types such as Ptr and Array . 它不适用于对Int s, Char s, Float或不可表示类型(如PtrArray列表进行排序。 It could probably be made a bit more efficient by using an algorithm which uses the number of possible constructors to push the linear constant down further but IntMap will have to do for now :-) 通过使用一种算法可以使效率更高一些,该算法使用可能的构造函数来进一步推动线性常量,但IntMap现在必须这样做:-)

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

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