繁体   English   中英

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

[英]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.TreeTree数据类型的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM