List 将括号字符串解析为Haskell中的嵌套列表

List 将括号字符串解析为Haskell中的嵌套列表,list,parsing,haskell,infinite,List,Parsing,Haskell,Infinite,我的目标是编写一个函数,将嵌套括号的字符串解析到相应的列表中: parseParens "()" --> [] parseParens "(())" --> [[]] parseParens "((()()))" --> [[[],[]]] 首先,我发现我不能简单地指定返回值的类型。我可以这样做: parseParens :: String -> [[[[t]]]] 但是我怎么说它是无限嵌套的呢?我猜哈斯克尔不允许这样 我的解决方案 我提出了自己的数据类型: data

我的目标是编写一个函数,将嵌套括号的字符串解析到相应的列表中:

parseParens "()" --> []
parseParens "(())" --> [[]]
parseParens "((()()))" --> [[[],[]]]
首先,我发现我不能简单地指定返回值的类型。我可以这样做:

parseParens :: String -> [[[[t]]]]
但是我怎么说它是无限嵌套的呢?我猜哈斯克尔不允许这样

我的解决方案 我提出了自己的数据类型:

data InfiniteList = EmptyList | Cons InfiniteList InfiniteList deriving (Show)
以及一个使用以下内容的解析器函数:

parseParens :: String -> InfiniteList
parseParens ('(':xs) =
    if remainder == ""
        then result
        else error "Unbalanced parenthesis"
    where (result, remainder) = parseToClose EmptyList xs
parseParens _ = error "Unbalanced parenthesis"

parseToClose :: InfiniteList -> String -> (InfiniteList, String)
parseToClose acc "" = error "Unbalanced parenthesis!"
parseToClose acc (')':xs) = (acc, xs)
parseToClose acc ('(':xs) = parseToClose (concatInfLists acc (Cons result EmptyList)) remainder
    where (result, remainder) = parseToClose EmptyList xs

concatInfLists :: InfiniteList -> InfiniteList -> InfiniteList
concatInfLists EmptyList ys = ys
concatInfLists (Cons x xs) ys = Cons x (concatInfLists xs ys)
像这样工作:

parseParens "()" --> EmptyList
parseParens "(())" --> Cons EmptyList EmptyList
parseParens "((()()))" --> Cons (Cons EmptyList (Cons EmptyList EmptyList)) EmptyList
如何改进? 肯定有更好的方法来做到这一点。也许有一种方法可以使用内置的列表数据类型来实现这一点?

编辑:修复了我对Benjamin答案的错误描述

而@Benjamin Hodgson评论中的答案是:

data Nested a = Flat a | Nested (Nested [a]) deriving (Show)
提供了一种很好的方法来表示任意嵌套深度的同质列表(即,有点像是一种总和类型的
[a]
加上
[[a]]
加上
[[a]]]]
加上所有其他类型),对于您的问题来说,这似乎是一种不寻常的表示,尤其是在以下情况下:

parseParens "(()(()))"
其中“子节点”的嵌套深度不同。这将表现为:

Nested (Nested (Nested (Flat [[],[[]]]))) :: Nested a
因此,如果有足够的
嵌套的
构造函数,它实际上允许您将解析结果表示为所需的列表,但它有一些奇怪的属性。例如,最里面的空列表实际上有不同的类型:第一个是
[[a]]
类型,而第二个是
[a]
类型

作为一种替代方法,我认为您实际想要的数据类型可能只是:

data Nested = N [Nested] deriving (Show)
其中,每个节点
N
是一个节点列表(可能为空)。然后,您将得到:

> parseParens "()"
N []
> parseParens "(())"
N [N []]
> parseParens "((()()))"
N [N [N [],N []]]
> parseParens "(()(()))"
N [N [],N [N []]]
如果忽略这些结果中的
N
构造器,那么前三个构造器将匹配问题开头的“对应列表”测试用例

作为旁注:上面的
嵌套的
数据类型实际上是一个不包含数据的“玫瑰树”,相当于使用
容器
包中的
数据类型

最后,我再怎么强调学习和使用一元解析库的帮助也不为过,即使对于简单的解析工作也是如此。例如,使用
parsec
库,您可以在一行中为语法编写解析器:

nested = N <$> between (char '(') (char ')') (many nested)

数据嵌套a=平面a |嵌套(嵌套[a])
嵌套的A
值由n个
嵌套的
构造函数组成,由一个
平面
构造函数终止,该构造函数包含一个n深嵌套的
A
s列表。这个
N[]
东西很好用。我成功地将它插入到我现有的代码中。我肯定还需要学习这些解析库。。。不过首先我需要考虑一下monads。创建新数据类型有什么特别的原因吗。在我看来,解析到列表就足够了。@user26732,最初的问题是决定给什么类型的解析器。例如,如果它的类型为
parseParens::String->[[[a]]]]]
,则可以返回普通列表,但最多只能返回深度为4的列表。
import Data.Tree
import Text.Parsec
import Text.Parsec.String

data Nested = N [Nested] deriving (Show)

nested :: Parser Nested
nested = N <$> between (char '(') (char ')') (many nested)

parseParens :: String -> Nested
parseParens str =
  let Right result = parse (nested <* eof) "" str
  in  result