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

时间:2014-10-22 05:15:23

标签: haskell types generic-programming

给定此数据类型

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

以及

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

如何将vals中的元素分组到此列表中

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

3 个答案:

答案 0 :(得分:13)

要添加到@ RamonSnir的答案,还可以使用" Scrap你的样板自动构建用于按构造函数对数据类型进行分组的功能"框架:

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

两个重要部分是:

  • 派生TypeableData以及
  • 使用toConstr来获取特定值中使用的构造函数的表示。

答案 1 :(得分:7)

我有以下内容:

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

然后:

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

答案 2 :(得分:1)

(这可能是极端过度杀伤,但修补它是一个有趣的问题!)

提供的答案有三个小问题:

  • 如果所考虑的类型不在Ord中(例如,某处有某个功能)会怎样?
  • 此外,此操作应该是列表长度为O(n log n)吗?
  • 最后,提供的示例不足以确定分组是否应该是稳定的,即:分组[X 2, X 1]的结果应该是[X 1, X 2](那就是你的意思如果您使用基于sort的解决方案,或者元素是否应保持原始顺序?

所以这是我能提出的最通用的解决方案。它很稳定,它不需要Ord(实际上你甚至不需要触摸原始数据类型)它运行在大约O(n * min(n,W) ))W是你的机器的字大小的时间(在我的,它的64)。也就是说,一旦列表比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]

现在groupByConstructor vals提供[[X 2, X 1],[Y True, Y False],[Z 2.7, Z 3.14, Z 0.2]],我认为应该这样做。


它不适用于排序IntCharFloat或不可表示类型(例如Ptr和{{}的列表1}}。通过使用一种算法可以使效率更高一些,该算法使用可能的构造函数来进一步推动线性常数,但Array现在必须做: - )