【问题标题】:Nested chunksOf in Haskell?Haskell中的嵌套块?
【发布时间】:2015-05-31 03:12:08
【问题描述】:

说我想这样做:

nestedChunksOf [3, 2] [1,1,1,2,2,2,3,3,3,4,4,4] == [[[1,1,1], [2,2,2]], [[3,3,3], [4,4,4]]]

在 Python 中,我可以做到这一点

def group(a, *ns):
    for n in ns:
        a = [a[i:i+n] for i in xrange(0, len(a), n)]
    return a

group([1,1,1,2,2,2,3,3,3,4,4,4], 3, 2) == [[[1,1,1],[2,2,2]],[[3,3,3],[4,4,4]]]

但在 Haskell 中,我不能只说

nestedChunksOf :: [Int] -> [a] -> [[a]]

nestedChunksOf :: [Int] -> [a] -> [[[a]]]

那么我怎样才能在 Haskell 中实现同样的目标呢?

【问题讨论】:

    标签: list haskell nested


    【解决方案1】:

    nestedChunksOf 这样的函数不能直接在Haskell 中完成,至少不能在普通列表上运行。列表的深度是类型的一部分,因此不能通过参数指定任意深度。

    但是你可以做的是嵌套chunksOf

    如果我们这样定义chunksOf

    chunksOf :: Int -> [a] -> [[a]]
    chunksOf _ [] = []
    chunksOf n xs = fxs : chunksOf n sxs
        where (fxs, sxs) = splitAt n xs
    

    然后我们可以嵌套它:

    Main> :l test.hs
    [1 of 1] Compiling Main             ( test.hs, interpreted )
    Ok, modules loaded: Main.
    *Main> chunksOf 3 [1,1,1,2,2,2,3,3,3,4,4,4]
    [[1,1,1],[2,2,2],[3,3,3],[4,4,4]]
    *Main> chunksOf 2 $ chunksOf 3 [1,1,1,2,2,2,3,3,3,4,4,4]
    [[[1,1,1],[2,2,2]],[[3,3,3],[4,4,4]]]
    

    我希望能实现你想要的!

    【讨论】:

      【解决方案2】:

      正如其他答案中所述,这不能像在 Haskell 中那样直接完成,您始终需要知道表达式的类型,从而区分 [a][[a]] 等。但是,使用 polymorphic recursion 你可以通过将每个级别包装在构造函数中来定义允许这种任意嵌套的数据类型:

      data NestedList a = Value a | Nested (NestedList [a])
        deriving (Show)
      

      所以只要Valuea 同构,Nested (Value ...)[a] 同构,将Nested 加倍到[[a]] 等等。然后你可以实现

      chunksOf :: Int -> [a] -> [[a]]
      ...
      
      nestedChunksOf :: [Int] -> [a] -> NestedList a
      nestedChunksOf [] xs = Nested (Value xs)
      nestedChunksOf (c:cs) xs = Nested (nestedChunksOf cs $ chunksOf c xs)
      

      确实

      print $ nestedChunksOf [3, 2] [1,1,1,2,2,2,3,3,3,4,4,4]
      

      输出

      Nested (Nested (Nested (Value [[[1,1,1],[2,2,2]],[[3,3,3],[4,4,4]]])))
      

      【讨论】:

        【解决方案3】:

        使用依赖类型可以很容易地做到这一点。

        我们想表达[Int] 参数的长度决定了结果的类型。为此,我们需要两件事:一个具有固定长度的列表类型,以及一个根据长度计算返回类型的类型级函数:

        {-# LANGUAGE DataKinds, GADTs, TypeFamilies #-}
        
        import Data.List.Split
        
        data Nat = Z | S Nat -- natural numbers (zero, successor)
        
        data Vec n a where -- "n" length lists of "a" elements
          Nil  :: Vec Z a
          (:>) :: a -> Vec n a -> Vec (S n) a
        infixr 5 :>
        
        type family Iterate n f a where
          Iterate Z     f a = a
          Iterate (S n) f a = f (Iterate n f a)
        

        Iterate n f a 将类型构造函数 f n 应用于参数。例如,Iterate (S (S Z)) [] Int 简化为 [[Int]]nestedChunksOf现在可以直接写了:

        nestedChunksOf :: Vec n Int -> [a] -> Iterate (S n) [] a
        nestedChunksOf Nil       as = as
        nestedChunksOf (n :> ns) as = chunksOf n $ nestedChunksOf ns as
        

        用法:

        > nestedChunksOf (2 :> 3 :> Nil) [1,1,1,2,2,2,3,3,3,4,4,4]
        [[[1,1,1],[2,2,2]],[[3,3,3],[4,4,4]]]
        

        【讨论】:

        • 这太棒了!类型族的奇妙使用。
        【解决方案4】:

        这在 Haskell 中无法通过“普通”方式实现,因为它需要依赖类型 - 结果的类型取决于第一个参数的长度。

        也许元组解决方案可以接受?

        {-# Language TypeFamilies #-}
        {-# Language FlexibleInstances #-}
        import Data.List.Split
        class NestedChunksOf a where
            nco :: a -> [b] -> AList a b
            type AList a b :: *
        
        instance NestedChunksOf (Int,Int) where
            nco (f,s) xs = chunksOf f (chunksOf s xs)
            type AList (Int,Int) a = [[[a]]]
        
        
        -- More instances as desired.
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-09-12
          • 2020-03-29
          • 1970-01-01
          • 2022-01-20
          • 1970-01-01
          • 2014-06-04
          • 2019-02-27
          • 1970-01-01
          相关资源
          最近更新 更多