【问题标题】:Is there a function to flatten a nested list of elements?是否有展平嵌套元素列表的功能?
【发布时间】:2011-08-25 01:01:08
【问题描述】:

我怎样才能像这样展平嵌套列表:

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

【问题讨论】:

  • 您拥有的是列表列表,因此您要求展平两个级别。
  • 值得一提的是,任意嵌套的列表可以看成一棵树,很容易展平(只需递归到叶子节点)。

标签: list haskell


【解决方案1】:

是的,是标准前奏曲中的concatgiven by

concat :: [[a]] -> [a]
concat xss = foldr (++) [] xss

如果要将[[[a]]]变成[a],必须使用两次:

Prelude> (concat . concat) [[[1,2],[3]],[[4]]]
[1,2,3,4]

【讨论】:

    【解决方案2】:

    由于没有其他人给出这个,因此可以定义一个函数,通过使用 MultiParamTypeClasses 将任意深度的列表展平。我实际上并没有发现它有用,但希望它可以被认为是一个有趣的 hack。我从 Oleg 的多变量函数实现中得到了这个想法。

    {-# LANGUAGE MultiParamTypeClasses, OverlappingInstances, FlexibleInstances #-}
    
    module Flatten where
    
    class Flatten i o where
      flatten :: [i] -> [o]
    
    instance Flatten a a where
      flatten = id
    
    instance Flatten i o => Flatten [i] o where 
      flatten = concatMap flatten
    

    现在如果你加载它并在 ghci 中运行:

    *Flatten> let g = [1..5]
    *Flatten> flatten g :: [Integer]
    [1,2,3,4,5]
    *Flatten> let h = [[1,2,3],[4,5]]
    *Flatten> flatten h :: [Integer]
    [1,2,3,4,5]
    *Flatten> let i = [[[1,2],[3]],[],[[4,5],[6]]]
    *Flatten> :t i
    i :: [[[Integer]]]
    *Flatten> flatten i :: [Integer]
    [1,2,3,4,5,6]
    

    请注意,通常需要提供结果类型注释,否则 ghc 无法确定在哪里停止递归应用 flatten 类方法。但是,如果您使用具有单态类型的函数就足够了。

    *Flatten> :t sum
    sum :: Num a => [a] -> a
    *Flatten> sum $ flatten g
    
    <interactive>:1:7:
        No instance for (Flatten Integer a0)
          arising from a use of `flatten'
        Possible fix: add an instance declaration for (Flatten Integer a0)
        In the second argument of `($)', namely `flatten g'
        In the expression: sum $ flatten g
        In an equation for `it': it = sum $ flatten g
    *Flatten> let sumInt = sum :: [Integer] -> Integer
    *Flatten> sumInt $ flatten g
    15
    *Flatten> sumInt $ flatten h
    15
    

    【讨论】:

    • 非常有趣。我以为你必须求助于 Template Haskell 来做这种事情。
    • @Dan:OverlappingInstances 扩展允许在类型构造函数上进行模式匹配的能力大致接近,但需要注意的是,案例是根据具体情况而不是定义顺序来选择的。这是一种非常“直截了当”的可怕类型级元编程骇客,有时实际上很有用。通过嵌套的(-&gt;)s 递归允许可变参数函数,或者使用嵌套元组的东西,如异构列表。这一切都非常愉快。
    • 是的,一个有趣的 hack,但我不建议将它用于实际编程。 -1 只是因为这是得分最高的答案,它不应该是。 (你可以处理 -2 rep :-)
    • @luqui:正如我所说,我从来没有发现这很有用。但这可能是我获得“纪律严明”徽章的机会。
    • @CoolCodeBro,是的,而且它与类型推断的交互很差(所以如果你在没有签名的 where 辅助函数中使用它,你可能会发生一些令人困惑的事情),而且它与多态性的交互很差(如文章末尾的注释中所示)。如果您真的想了解它的问题,只需尝试在您认为需要它的任何地方使用它。你将学习。 ;)
    【解决方案3】:

    正如其他人所指出的,concat :: [[a]] -&gt; [a] 是您正在寻找的功能,它不能展平任意深度的嵌套列表。您需要多次调用它才能将其压平到所需的水平。

    不过,该操作确实可以推广到其他 monad。然后它被称为join,类型为Monad m =&gt; m (m a) -&gt; m a

    Prelude Control.Monad> join [[1, 2], [3, 4]]
    [1,2,3,4]    
    Prelude Control.Monad> join (Just (Just 3))
    Just 3
    Prelude Control.Monad.Reader> join (+) 21
    42
    

    【讨论】:

    • 我没有得到第三个例子......有人可以更详细地解释一下吗?
    • @jhegedus (+) 的类型是a -&gt; a -&gt; aa -&gt; (a -&gt; a) 相同 再上一步:(a-&gt;) ( (a-&gt;) a ),你看到这和[[a]] 的相似之处了吗?同[] ([] a)?
    【解决方案4】:
    import Data.List
    let flatten = intercalate []
    
    flatten $ flatten [[[1,2],[3]],[[4]]]
    [1,2,3,4]
    

    【讨论】:

    • 你为什么要用这个而不是concat
    【解决方案5】:

    正如 hammar 所指出的,join 是扁平化列表的“单子”方式。您也可以使用do-Notation 来轻松编写多个级别的扁平化函数:

    flatten xsss = do xss <- xsss
                      xs <- xss
                      x <- xs
                      return x
    

    【讨论】:

    • 5 年后,但您可以简化:flatten = ((id =&lt;&lt;) =&lt;&lt;)
    【解决方案6】:

    任意嵌套的列表可以近似为Data.Tree,可以通过适当命名的函数flatten 展开。

    我说近似是因为Data.Tree 允许将数据项附加到每个节点,而不仅仅是叶子。但是,您可以创建一个Data.Tree (Maybe a),并将Nothing 附加到正文节点,然后用catMaybes . flatten 展平。

    【讨论】:

      【解决方案7】:

      您可以使用concat 删除一层嵌套,因此您可以通过应用concat n 次来应用n 层嵌套。

      不可能编写删除任意级别嵌套的函数,因为无法使用 Haskell 的类型系统(使用列表数据类型 - 您可以为任意嵌套列表编写自己的数据类型,并为此编写一个展平函数。

      【讨论】:

        猜你喜欢
        • 2018-03-08
        • 2023-04-03
        • 1970-01-01
        • 1970-01-01
        • 2018-07-04
        • 1970-01-01
        • 2020-10-11
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多