Better Way to Write "Matching Balanced Parenthesis" Program in Ruby

Matching balanced parenthesis in Ruby using recursive regular expressions like perl

Yes. With oniguruma regex engine, which is built in in Ruby 1.9, and is installable on Ruby 1.8, you can do that. You name a subregex with (?<name>...) or (?'name'...). Then you call a subregex with \g<name> or \g'name' within the same regex. So your regex translated to oniguruma regex will be:

re = %r{
(?<re>
\(
(?:
(?> [^()]+ )
|
\g<re>
)*
\)
)
}x

Also note that multi-byte string module in PHP >=5 uses oniguruma regex engine, so you will be able to do the same.

The manual for oniguruma is here.

Checking if a string has balanced parentheses

Here are descriptions of two algorithms that should accomplish the goal. I'll leave it as an exercise to the reader to turn them into code (unless you explicitly ask for a code solution):

  1. Start with a variable set to 0 and loop through each character in the string: when you see a '(', add one to the variable; when you see a ')', subtract one from the variable. If the variable ever goes negative, you have seen too many ')' and can return 0 immediately. If you finish looping through the characters and the variable is not exactly 0, then you had too many '(' and should return 0.

  2. Remove every occurrence of '()' in the string (replace with ''). Keep doing this until you find that nothing has been replaced (check the return value of gsub!). If the string is empty, the parentheses were matched. If the string is not empty, it was mismatched.

Balanced brackets conditional issue

The if statement if bracket == "{" or "[" or "(" needs to have a bracket == X for each or condition in the statement. Change:

if bracket == "{" or "[" or "("

to

if bracket == "{" or bracket == "[" or bracket ==  "("

...and that should work. The full code would be:

def balanced?(list_of_brackets)
if list_of_brackets.length % 2 != 0
false
else
stack = []
bracket_sets = {
'{' => '}',
'[' => ']',
'(' => ')'
}
list_of_brackets.chars do |bracket|
if bracket == "{" or bracket == "[" or bracket == "("
puts "#{bracket} is an opening bracket"
else
puts "#{bracket} is a closing bracket"
end
end
end
stack.empty?
end

puts balanced?('{}()[]')

Ruby - Regex for matching brackets?

non_delimiters = /[^(){}\[\]]*/
Paired = /\(#{non_delimiters}\)|\{#{non_delimiters}\}|\[#{non_delimiters}\]/
Delimiter = /[(){}\[\]]/

def balanced? s
s = s.dup
s.gsub!(Paired, "".freeze) while s =~ Paired
s !~ Delimiter
end

balanced?(")([]{}")
# => false
balanced?("[]")
# => true
balanced?("[()]")
# => true
balanced?("{}")
# => true
balanced?("}{")
# => false
balanced?("}[]}")
# => false
balanced?("[[]]")
# => true

Regular expression to match balanced parentheses

Regular expressions are the wrong tool for the job because you are dealing with nested structures, i.e. recursion.

But there is a simple algorithm to do this, which I described in more detail in this answer to a previous question. The gist is to write code which scans through the string keeping a counter of the open parentheses which have not yet been matched by a closing parenthesis. When that counter returns to zero, then you know you've reached the final closing parenthesis.

Simple Ragel Example that Balances Parentheses?

A general pattern using fcall/fret is like:

balanced = [^(){}\[\]] |
'(' @{ fcall balancedTokensParen; } |
'[' @{ fcall balancedTokensBracket; } |
'{' @{ fcall balancedTokensBrace; };
balancedTokensParen := balanced* ')' @{ fret; };
balancedTokensBracket := balanced* ']' @{ fret; };
balancedTokensBrace := balanced* '}' @{ fret; };

So your case could be handled as

  char = [xo];
group = '(' @{ fcall group_rest; };
group_rest := (char|group)* ')' @{ fret; };

main := group;

the lexer function should contain the stack array, and you have to manually to check the top to ensure there isn't unclosed '(':

stack = []
%% write init;
%% write exec;
if top > 0
cs = %%{ write error; }%%
end

Search for matching parenthesis

It is not possible to do this correctly without a stack under one form or another. The way you are doing it does not cope correctly with nestings. For example it will incorrectly accept "[(])"

Detecting bad syntax in a string

[Edit: I've incorporated both of @sawa's excellent suggestions.]

One way you can do this is with a stack.

MATCH   = { '['=>']', '('=>')', '{'=>'}' }
OPENING = MATCH.keys
CLOSING = MATCH.values

def check_for_match(str)
str.chars.each_with_object([]) do |c, arr|
case c
when *OPENING
arr << c
when *CLOSING
return false unless c.eql?(MATCH[arr.pop])
end
end.empty?
end

check_for_match("zx(c)abcde[z{x]}") #=> false
check_for_match("zx(c)abcde[z{x}]") #=> true


Related Topics



Leave a reply



Submit