I wrote the following program to check strings for balanced parenthesis:
isBalanced xs = isBalanced\' xs []
isBalanced\' [] [] = True
isBalanced\' [] _ = False
As Henning said, parser combinators would work for this. Here's an example using 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
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
The fold builds up a stack of previously encountered characters, stripping away any matches as it finds them. If you end up with an empty list, the string is balanced.
The disadvantage of using a left fold is that the entire string must always be scanned through. It would be nice to abort the operation with a failure should a closing brace be found without a matching opening brace. Here's a version that does just that.
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)
Probably overkill for a problem this simple, but you could try looking up parser combinators.
As a more elementary simplification, you could rewrite your recursion into folding over a function that takes a stack and a character from the string to a new stack. (Whether this would make actually it simpler is in the eye of the beholder, though).
I think hammar's answer is the best, but here are smaller steps you can do - use null
and lookup
:
{-# 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
Your example positives
and negatives
data should definitely use map
, or even all
.