检查字符串是否包含平衡括号

时间:2011-08-26 18:51:49

标签: haskell recursion pattern-matching formal-languages pushdown-automaton

我编写了以下程序来检查平衡括号的字符串:

isBalanced xs = isBalanced' xs []

isBalanced' [] [] = True
isBalanced' [] _  = False

isBalanced' ('(':xs) ys = isBalanced' xs (')':ys)
isBalanced' ('[':xs) ys = isBalanced' xs (']':ys)
isBalanced' ('{':xs) ys = isBalanced' xs ('}':ys)

isBalanced' _  [] = False

isBalanced' (x:xs) (y:ys) = (x == y) && (isBalanced' xs ys)

以下是一些示例数据:

positives = [
    isBalanced "",
    isBalanced "()",
    isBalanced "[]",
    isBalanced "{}",
    isBalanced "([]){}[{}]"
    ]

negatives = [
    isBalanced "(",
    isBalanced "[",
    isBalanced "{",
    isBalanced ")",
    isBalanced "]",
    isBalanced "}",
    isBalanced "([)]",
    isBalanced "{]",
    isBalanced ")("
    ]

由于这个程序只使用显式递归的最基本构建块,我想知道是否有一个更短,更高级的方法涉及我还不知道的语言设施。


好的,我从几个答案和评论(以及我自己的想法)中提炼出以下解决方案:

import Text.Parsec

grammar = many parens >> return () where
 parens = choice [ between (char opening) (char closing) grammar
                 | [opening, closing] <- ["()", "[]", "{}"]]

isBalanced = isRight . parse (grammar >> eof) ""

isRight (Right _) = True
isRight _         = False

4 个答案:

答案 0 :(得分:17)

作为Henning said,解析器组合器可以为此工作。以下是使用Parsec的示例:

import Text.Parsec

grammar = many braces >> return ()
    where braces = choice [ between (char '(') (char ')') grammar
                          , between (char '[') (char ']') grammar
                          , between (char '{') (char '}') grammar
                          ]

isBalanced :: String -> Bool
isBalanced input = case parse (grammar >> eof) "" input of
                       Left  _ -> False
                       Right _ -> True

答案 1 :(得分:10)

使用左侧折叠

import Data.List (foldl')

isBalanced xs = null $ foldl' op [] xs
  where
    op ('(':xs) ')' = xs
    op ('[':xs) ']' = xs
    op ('{':xs) '}' = xs
    op xs x = x:xs

折叠构建了一堆先前遇到的字符,在找到它们时剥离掉任何匹配项。如果您最终得到一个空列表,则该字符串是平衡的。

在Maybe monad中使用左侧折叠

使用左折叠的缺点是必须始终扫描整个字符串。如果在没有匹配的开括号的情况下找到闭合支撑,那么中止失败的操作会很好。这是一个可以做到这一点的版本。

import Control.Monad (foldM)

isBalanced' xs = maybe False null $ foldM op [] xs
  where
    op ('(':xs) ')'          = Just xs
    op ('[':xs) ']'          = Just xs
    op ('{':xs) '}'          = Just xs
    op xs x | x `elem` ")]}" = Nothing
            | otherwise      = Just (x:xs)

答案 2 :(得分:2)

这个问题可能有点过分,但您可以尝试查找parser combinators

作为一个更简单的简化,你可以将你的递归重写为折叠一个函数,该函数将一个堆栈和一个字符从字符串转移到一个新的堆栈。 (不过,这实际上是否会更容易实现它在旁观者的眼中)。

答案 3 :(得分:2)

我认为hammar的答案是最好的,但是您可以采取较小的步骤 - 使用nulllookup

{-# LANGUAGE PatternGuards #-}
isBalanced xs = isBalanced' xs []

isBalanced' [] x = null x

isBalanced' (c:xs) ys | Just d <- lookup c par = isBalanced' xs (d:ys)
  where par = [('(',')'), ('[',']'),('{','}')]

isBalanced' _  [] = False

isBalanced' (x:xs) (y:ys) = x == y && isBalanced' xs ysl

您的示例positivesnegatives数据肯定会使用map,甚至是all