[英]Parsing String of parenthesis to nested List in Haskell
我的目标是编写一个函数来将嵌套括号的字符串解析为相应的列表:
parseParens "()" --> []
parseParens "(())" --> [[]]
parseParens "((()()))" --> [[[],[]]]
首先我发现我无法指定轻松定义返回值的类型。 我可以这样做:
parseParens :: String -> [[[[t]]]]
但是我怎么说它是无限嵌套的呢? 我想Haskell不允许这样做。
我提出了自己的数据类型:
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
肯定有一个更好的方法来做到这一点。 也许有一种方法可以使用内置的List数据类型吗?
编辑 :修正了我对本杰明答案的错误描述。
虽然答案在@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
数据类型实际上是一个不包含数据的“玫瑰树”,相当于使用containers
包中Data.Tree
的Tree
数据类型的Tree ()
。
最后,我无法强调学习和使用monadic解析库是多么有用,即使对于简单的解析工作也是如此。 例如,使用parsec
库,您可以在一行中为语法编写解析器:
nested = N <$> between (char '(') (char ')') (many nested)
我的parseParens
完整代码是:
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.