簡體   English   中英

在Haskell中通過構造函數對數據類型進行分組

[英]Grouping data types by constructor in Haskell

給定此數據類型

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]]

要添加到@ 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

兩個重要部分是:

  • 派生可TypeableData ,和
  • 使用toConstr來獲取特定值中使用的構造函數的表示。

我有以下幾點:

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]]

(這可能是極端矯枉過正,但修補它是一個有趣的問題!)

提供的答案有3個小問題:

  • 如果所考慮的類型不在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]] ,我認為應該如此。


它不適用於對Int s, Char s, Float或不可表示類型(如PtrArray列表進行排序。 通過使用一種算法可以使效率更高一些,該算法使用可能的構造函數來進一步推動線性常量,但IntMap現在必須這樣做:-)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM