我的目标是编写一个函数来将嵌套括号的字符串解析成相应的列表:
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数据类型吗?
答案 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