作为一种类型两轮牛车的锻炼,可以与标准的列表,以实现这一点。
所有我们需要的是一个任意深度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
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)
最后宽:
然后用我们的谓词类型type Pred = String -> String -> Bool
自定义规则展开说唱它在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
因此,有可能(也可能可以简化),但一如既往地与这类两轮牛车不推荐用于任何东西,但锻炼。
有趣的问题。当你说“它不是预先知道的”,你的意思是在编译时?如果是这样,那么你可能会失败,因为haskell是静态类型的。 – jberryman 2012-08-07 17:17:34
在C/C++中一个列表通常是一个数组,一个数组通常是一个2维矩阵,使得数组列表意味着你正在增加维数1,从2到3,数组列表是一个3D矩阵从抽象的角度来看);我不知道Haskell,但可能你的问题只是矩阵/矢量的维度。 – user827992 2012-08-07 17:23:47
@ user827992,在Haskell中,列表是一个列表,而不是一个数组。(这是一个单链表,准确地说) – dflemstr 2012-08-07 17:42:28