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

时间:2017-11-30 15:36:52

标签: list parsing haskell infinite

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

std::async

首先我发现我无法指定轻松定义返回值的类型。我可以做类似的事情:

parseParens "()" --> []
parseParens "(())" --> [[]]
parseParens "((()()))" --> [[[],[]]]

但是我怎么说它是无限嵌套的呢?我想Haskell不允许这样做。

我的解决方案

我提出了自己的数据类型:

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)

如何改进?

肯定有更好的方法来做到这一点。也许有一种方法可以使用内置的List数据类型吗?

1 个答案:

答案 0 :(得分:7)

编辑:修正了我对本杰明答案的错误描述。

@Benjamin Hodgson评论中的回答:

data Nested a = Flat a | Nested (Nested [a]) deriving (Show)

提供了一种表示任意嵌套深度的同类列表的好方法(即类似于[a][[a]][[[a]]]加上所有其余的总和类型),它对你的问题来说似乎是一种不同寻常的表现形式,特别是在以下情况下:

parseParens "(()(()))"

其中“子节点”的嵌套深度不同。这将表示为:

Nested (Nested (Nested (Flat [[],[[]]]))) :: Nested a

因此,它允许您将解析的结果表示为所需的列表,给定足够的Nested构造函数,但它具有一些奇怪的属性。例如,最里面的空列表实际上有不同的类型:第一个是[[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构造函数,则前三个与您的问题开头的“对应列表”测试用例相匹配。

作为旁注:上面的Nested数据类型实际上是一个不包含数据的“玫瑰树”,相当于Tree ()使用来自Tree的{​​{1}}数据类型在Data.Tree包中。

最后,我无法强调学习和使用monadic解析库是多么有用,即使对于简单的解析工作也是如此。例如,使用containers库,您可以在一行中为语法编写解析器:

parsec

nested = N <$> between (char '(') (char ')') (many nested) 的完整代码是:

parseParens