列表清单列表

时间:2012-08-07 17:08:52

标签: haskell

表示作为列表列表的类型LoL a的好方法是什么 ...... a的......嵌套级别是任意的,但总体上是统一的 外部清单的要素。

我想到的情况是对a的成员应用分组 列表,然后在每个子组上应用下一个分组,依此类推。它 事先不知道有多少人必须申请。因此:

rGroupBy :: [(a -> a -> Bool)] -> [a] -> [...[a]...]

rGroupBy的类型签名的额外布朗尼点; - )

示例:

假设deweyGroup i根据第i个数字

对元素进行分组
rGroupBy [deweyGroup 1, deweyGroup 2] 
         ["1.1", "1.2.1", "1.2.2", "2.1", "2.2", "3"]

给出:

[ [ [ "1.1" ], [ "1.2.1", "1.2.2" ] ],
  [ [ "2.1" ], [ "2.2" ] ],
  [ [ "3" ] ]
]

后记

一天后,我们有4个优秀且互补的解决方案。我很满意答案;谢谢大家。

5 个答案:

答案 0 :(得分:11)

强制执行约束的另一种方法是使用嵌套数据类型:

data LoL a = One [a] | Many (LoL [a])

mapLoL :: ([a] -> [b]) -> LoL a -> LoL b
mapLoL f (One xs) = One (f xs)
mapLoL f (Many l) = Many $ mapLoL (map f) l

rGroupBy :: [a -> a -> Bool] -> [a] -> LoL a
rGroupBy [] xs = One xs
rGroupBy (f:fs) xs = Many $ mapLoL (groupBy f) $ rGroupBy fs xs

扩展LoL的定义,我们非正式地看到,

LoL a = [a] | [[a]] | [[[a]]] | ...

然后我们可以说,例如:

ghci> rGroupBy [(==) `on` fst, (==) `on` (fst . snd)] [ (i,(j,k)) | i<-[1..3], j<-[1..3], k<-[1..3]]

回来

Many (Many (One [[[(1,(1,1)),(1,(1,2)),(1,(1,3))]],[[(1,(2,1)),(1,(2,2)),(1,(2,3)), ...

答案 1 :(得分:9)

你实际拥有的是一棵树。尝试用递归数据结构表示它:

data LoL a = SoL [a] | MoL [LoL a] deriving (Eq, Show)

rGroupBy :: [(a -> a -> Bool)] -> [a] -> LoL a
rGroupBy (f:fs) = MoL . map (rGroupBy fs) . groupBy f
rGroupBy []     = SoL

deweyGroup :: Int -> String -> String -> Bool
deweyGroup i a b = a!!idx == b!!idx where idx = 2*(i-1)

rGroupBy [deweyGroup 1, deweyGroup 2] ["1.1", "1.2.1", "1.2.2", "2.1", "2.2", "3.0"]给出:

MoL [MoL [SoL ["1.1"],
          SoL ["1.2.1","1.2.2"]],
     MoL [SoL ["2.1"],
          SoL ["2.2"]],
     MoL [SoL ["3.0"]]
    ]

答案 2 :(得分:7)

如果要强制统一深度,有一个(相当)标准技巧可以做到涉及多态递归。我们所做的是拥有更深层次的内容。构造函数告诉列表有多深入嵌套,然后是最终的#34; here&#34;具有深层嵌套列表的构造函数:

data GroupList a = Deeper (GroupList [a]) | Here a deriving (Eq, Ord, Show, Read)

实际上,定义的类型有一个美学选择,您可能希望在代码中有所不同:Here构造函数只需a而不是a列表。这个选择的后果在这个答案的其余部分分散了。

这是展示列表清单的此类价值的一个例子;它有两个Deeper构造函数,对应于它具有的深度二嵌套:

> :t Deeper (Deeper (Here [[1,2,3], []]))
Num a => GroupList a

这里有一些示例函数。

instance Functor GroupList where
    fmap f (Here   a ) = Here   (f a)
    fmap f (Deeper as) = Deeper (fmap (fmap f) as)
    -- the inner fmap is at []-type

-- this type signature is not optional
flatten :: GroupList [a] -> GroupList a
flatten (Here   a ) = Deeper (Here a)
flatten (Deeper as) = Deeper (flatten as)

singleGrouping :: (a -> a -> Bool) -> GroupList [a] -> GroupList [a]
singleGrouping f = flatten . fmap (groupBy f)

rGroupBy :: [a -> a -> Bool] -> [a] -> GroupList [a]
rGroupBy fs xs = foldr singleGrouping (Here xs) fs

答案 3 :(得分:3)

我认为以下示例应该与您的想法接近。首先,我们声明类型级自然数。然后我们定义向量,它们将其长度作为幻像类型(参见Fixed-length vectors in Haskell, Part 1: Using GADTs)。然后我们为...列表的嵌套列表定义一个结构,它将深度作为幻像类型。最后,我们可以正确定义rGroupBy

{-# LANGUAGE GADTs #-}
{-# LANGUAGE EmptyDataDecls #-}

import Data.List (groupBy)

data Zero
data Succ n

data Vec n a where
    Nil  ::                 Vec Zero a
    Cons :: a -> Vec n a -> Vec (Succ n) a

data LList n a where
    Singleton :: a           -> LList Zero a
    SuccList  :: [LList n a] -> LList (Succ n) a

-- Not very efficient, but enough for this example.
instance Show a => Show (LList n a) where
    showsPrec _ (Singleton x)   = shows x
    showsPrec _ (SuccList lls)  = shows lls

rGroupBy :: Vec n (a -> a -> Bool) -> [a] -> LList (Succ n) a
rGroupBy Nil
    = SuccList . map Singleton
rGroupBy (Cons f fs)
    = SuccList . map (rGroupBy fs) . groupBy f

-- TEST ------------------------------------------------------------

main = do
    let input = ["1.1", "1.2.1", "1.2.2", "2.1", "2.2", "3"]

    -- don't split anything
    print $ rGroupBy Nil input
    -- split on 2 levels
    print $ rGroupBy (Cons (deweyGroup 1) 
                           (Cons (deweyGroup 2) Nil))
               input 
  where
    deweyGroup :: Int -> String -> String -> Bool
    deweyGroup i a b = a!!idx == b!!idx where idx = 2*(i-1)

答案 4 :(得分:1)

作为类型hackery练习,可以使用标准列表来实现它。

我们所需要的是一个任意深度的groupStringsBy函数:

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts,
  UndecidableInstances, IncoherentInstances,
  TypeFamilies, ScopedTypeVariables #-}

import Data.List
import Data.Function

class StringGroupable a b where
    groupStringBy :: Pred -> a -> b

instance (StringGroupable a b, r ~ [b]) => StringGroupable [a] r where
    groupStringBy f = map (groupStringBy f)

instance (r ~ [[String]]) => StringGroupable [String] r where
    groupStringBy p = groupBy p

其中的工作原理如下:

*Main> let lst = ["11","11","22","1","2"]
*Main> groupStringBy ((==) `on` length) lst
[["11","11","22"],["1","2"]]
*Main> groupStringBy (==) . groupStringBy ((==) `on` length) $ lst
[[["11","11"],["22"]],[["1"],["2"]]]

所以我们可以直接使用这个函数(尽管它必须按相反的顺序排列):

inp = ["1.1", "1.2.1", "1.2.2", "2.1", "2.2", "3"]

deweyGroup :: Int -> String -> String -> Bool
deweyGroup i a b = a!!idx == b!!idx where idx = 2*(i-1)

-- gives: [[["1.1"],["1.2.1","1.2.2"]],[["2.1"],["2.2"]],[["3"]]]
test1 = groupStringBy (deweyGroup 2) . groupStringBy (deweyGroup 1) $ inp

但是如果你想使用原始样本,我们也可以破解它。 首先,我们需要一个变量参数函数,它通过.以相反顺序对所有参数进行流水线操作,然后将最后一个参数应用于最后一个参数:

class App a b c r where
    app :: (a -> b) -> c -> r

instance (b ~ c, App a d n r1, r ~ (n -> r1)) => App a b (c -> d) r where
    app c f = \n -> app (f . c) n

instance (a ~ c, r ~ b) => App a b c r where
    app c a = c a

像这样工作:

*Main> app not not not True
False
*Main> app (+3) (*2) 2
10

然后使用我们的谓词类型type Pred = String -> String -> Bool的自定义规则展开它:

type Pred = String -> String -> Bool

instance (StringGroupable b c, App a c n r1, r ~ (n -> r1)) => App a b Pred r where
    app c p = app ((groupStringBy p :: b -> c) . c)

最后将其包装在rGroupBy中(提供id函数作为管道中的第一个):

rGroupBy :: (App [String] [String] Pred r) => Pred -> r
rGroupBy p = app (id :: [String] -> [String]) p

现在它应该适用于任何类型的Pred类型的分组谓词,产生的深度列表等于提供的谓词数:

-- gives: [["1.1","1.2.1","1.2.2"],["2.1","2.2"],["3"]]
test2 = rGroupBy (deweyGroup 1) inp

-- gives: [[["1.1"],["1.2.1","1.2.2"]],[["2.1"],["2.2"]],[["3"]]]
test3 = rGroupBy (deweyGroup 1) (deweyGroup 2) inp

-- gives: [[[["1.1"]],[["1.2.1","1.2.2"]]],[[["2.1"]],[["2.2"]]],[[["3"]]]]
test4 = rGroupBy (deweyGroup 1) (deweyGroup 2) (deweyGroup 1) inp

所以它有可能(并且可能会被简化)但是一如既往地使用这种hackery不建议用于除练习之外的任何东西。