给定此数据类型
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]]
答案 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
两个重要部分是:
答案 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
中(例如,某处有某个功能)会怎样? [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]]
,我认为应该这样做。
它不适用于排序Int
,Char
,Float
或不可表示类型(例如Ptr
和{{}的列表1}}。通过使用一种算法可以使效率更高一些,该算法使用可能的构造函数来进一步推动线性常数,但Array
现在必须做: - )