【问题标题】:Checking if a string consists of balanced parenthesis检查字符串是否由平衡括号组成
【发布时间】:2011-11-04 18:33:54
【问题描述】:

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

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 ")("
    ]

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


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

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

【问题讨论】:

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


    【解决方案1】:

    作为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
    

    【讨论】:

    • 甚至braces = choice [between (char o) (char c) grammar | (o,c) &lt;- [('(',')'),('[',']'),('{','}')]]
    【解决方案2】:

    使用左折叠

    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)
    

    【讨论】:

    • isBalanced "0" === False
    【解决方案3】:

    我认为 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

    【讨论】:

      【解决方案4】:

      对于这么简单的问题可能有点矫枉过正,但您可以尝试查找parser combinators

      作为更基本的简化,您可以将递归重写为折叠一个函数,该函数从字符串中获取一个堆栈和一个字符到一个新堆栈。 (不过,这是否会使它变得更简单,但在旁观者看来)。

      【讨论】:

      • 确实解析器组合器很有用。类似embrace s left right = char left *&gt; s &lt;* char right; balanced = empty &lt;|&gt; msum $ zipWith (embrace balanced) "([{" ")]}"
      • @Rotsor 该代码看起来不正确。 empty &lt;|&gt; ... 不应该是类似many (...) 的东西吗? (例如尝试解析"()()"
      • @Daniel,哦,确实……还有,运算符优先级是错误的。
      猜你喜欢
      • 2018-06-23
      • 1970-01-01
      • 2013-12-28
      • 2013-02-03
      • 2017-03-07
      • 2022-08-13
      • 2019-11-01
      • 2021-09-20
      • 2021-06-05
      相关资源
      最近更新 更多