作爲一種類型兩輪牛車的鍛鍊,可以與標準的列表,以實現這一點。
所有我們需要的是一個任意深度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