是否有一个函数来展平嵌套的元素列表?

56 haskell list

如何拼合这样的嵌套列表:

[1, 2, 3, 4] == flatten [[[1,2],[3]],[[4]]]
Run Code Online (Sandbox Code Playgroud)

Jos*_*Lee 112

是的,它concat来自标准序曲,.给出

concat :: [[a]] -> [a]
concat xss = foldr (++) [] xss
Run Code Online (Sandbox Code Playgroud)

如果你想[[[a]]]变成[a],你必须使用它两次:

Prelude> (concat . concat) [[[1,2],[3]],[[4]]]
[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)


Joh*_*n L 44

由于没有其他人给出这个,因此可以定义一个函数,该函数将使用MultiParamTypeClasses展平任意深度的列表.我实际上并没有发现它有用,但希望它可以被认为是一个有趣的黑客.我从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
Run Code Online (Sandbox Code Playgroud)

现在,如果你加载它并在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]
Run Code Online (Sandbox Code Playgroud)

请注意,通常需要提供结果类型注释,否则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
Run Code Online (Sandbox Code Playgroud)

  • 是的,一个有趣的黑客,但我不推荐它用于实际编程.-1只是因为这是得分最高的答案而且不应该是.(你可以处理-2代表:-) (4认同)
  • @Dan:`OverlappingInstances`扩展允许大致接近类型构造函数模式匹配的能力,但需要注意的是案例是通过特异性选择的,而不是定义的顺序.这是一个非常"直截了当"的可怕的类型级元编程hackery,偶尔实际上很有用.通过嵌套的`( - >)`递归允许可变参数函数,或者使用嵌套元组等异构列表.这一切都非常愉快. (2认同)

ham*_*mar 13

正如其他人所指出的那样,concat :: [[a]] -> [a]是您正在寻找的功能,它不能展平任意深度的嵌套列表.您需要多次调用它以将其展平至所需级别.

不过,该操作确实可以推广到其他monad.它被称为join,并具有类型Monad m => m (m a) -> 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
Run Code Online (Sandbox Code Playgroud)

  • 我没有得到第三个例子......有人可以更详细地解释一下吗? (5认同)
  • @jhegedus的类型`(+)`是`a - > a - > a`与`a - >(a - > a)相同``再一步:`(a->)((a- >)a)`,你看到这和`[[a]]`之间的相似性,它与`[]([] a)`相同吗? (3认同)

en4*_*4bz 9

import Data.List
let flatten = intercalate []

flatten $ flatten [[[1,2],[3]],[[4]]]
[1,2,3,4]
Run Code Online (Sandbox Code Playgroud)


Lan*_*dei 8

正如哈马尔指出的那样,join是一种压制名单的" monadic "方式.您也可以使用do-Notation来轻松拼写多个级别的功能:

flatten xsss = do xss <- xsss
                  xs <- xss
                  x <- xs
                  return x
Run Code Online (Sandbox Code Playgroud)

  • 5年后,你可以简化:```flatten =((id = <<)= <<)``` (3认同)