问题
I have an imaginary list with different levels of nesting, or ignoring the ugly types a API response ie:
a ::(Num a, Num [a], Num [[a]]) => [[a]]
a = [1, 2, [3, 4]]
b :: (Num a, Num [a], Num [[a]], Num [[[a]]]) => [[[[a]]]]
b = [[1,2,[3]],4]
The function I'm trying to create should do the following:
myFunc a == [1,2,3,4]
myFunc b == [1,2,3,4]
My initial thought was I'd have to parse the list into an AST (Abstract syntax tree) -->
use recursion to flatten all the branches & leaves into a single branch -->
parse the result back into a list.
I'm unsure how to parse the list into AST? or is there a better solution?
edit -- I think I was trying to be too literal, in that representing [1, 2, [3, 4]]
is actually part of the problem, so realistically for things to work better they would need to be represented as an ADT/AST. So if this was an API response or reading a file how would I parse that data into it's AST/ADT?
回答1:
An arbitrarily nested list won't type check. Each element of a list has to have the same type, but lists with different nesting levels have different types. The trick to get around this is to wrap a list into a new data type that hides the number of nested levels. But this is just a tree.
data Tree a = Root a | Branches [Tree a]
Then you can implement flatten
as a traversal of the tree.
flatten :: Tree a -> [a]
flatten (Root a) = [a]
flatten (Branches (t:ts)) = flatten t ++ (concat (fmap flatten ts))
See Data.Tree
in the containers package for a ready-to-use version.
For parsing, I would recommend using aeson. Data.Aeson.Types
defines the instance FromJSON v => FromJSON (Tree v)
, so you should just be able to use decode
on the json string and tell it you want a Tree Int
.
decode rawJson :: Maybe (Tree Int)
回答2:
This is already done for you by the GHC. Flattening is folding.
> :set -XDeriveFoldable
> data NList a = A a | N [NList a] deriving (Show, Functor, Foldable)
data NList a = A a | N [NList a]
> foldMap pure (N[ A 1, N[ A 2], A 3]) :: [Int]
[1,2,3]
> foldMap pure (N[ N[ N[ N[ A 1]]], N[ A 2], A 3]) :: [Int]
[1,2,3]
回答3:
Unclear what you're actually trying to achieve, but there is a syntax hack that actually allows you to write differently-nested list syntax in Haskell and have it flattened automatically:
{-# LANGUAGE TypeFamilies #-}
import GHC.Exts (IsList(..))
newtype AutoflatList a = AutoflatList {getFlatList :: [a]}
deriving (Show)
instance IsList (AutoflatList a) where
type Item (AutoflatList a) = AutoflatList a
fromList segs = AutoflatList $ getFlatList =<< segs
toList = pure
instance Num a => Num (AutoflatList a) where
fromInteger = AutoflatList . pure . fromInteger
*Main> :set -XOverloadedLists *Main> [1, 2, [3, 4]] :: AutoflatList Int AutoflatList {getFlatList = [1,2,3,4]} *Main> [[1,2,[3]],4] :: AutoflatList Int AutoflatList {getFlatList = [1,2,3,4]}
This solution not recommended except for recreational purposes.
来源:https://stackoverflow.com/questions/53062580/flatten-randomly-nested-list-into-a-non-nested-list-using-haskell