表示作为列表列表的类型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个优秀且互补的解决方案。我很满意答案;谢谢大家。
答案 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不建议用于除练习之外的任何东西。