Better way to write “matching balanced parenthesis” program in Ruby

♀尐吖头ヾ 提交于 2019-11-30 16:20:05

问题


This method is supposed to take a string and detect if the brackets '(' '{' '[' in the string are closing properly with the corresponding (opposite) brackets.

First, is there a more elegant, compact way to write this bit without using all the "or"s (||):

            split_array.each do |i| 
              if (i == "{" || i == "(" || i == "[")
                  left.push(i)
                else (i == "}" || i == ")" || i == "]")
                  right.push(i)
                end
             end

My second question is, is this code terrible (see below)? It seems I should be able to write this in way fewer lines, but logically, I haven't come up with another solution (yet.) The code works for most tests, but it returns false for this test (see all driver tests at bottom): p valid_string?("[ ( text ) {} ]") == true

Any critique would be greatly appreciated! (also, if there is a better section to post this, please let me know) Thanks!

def valid_string?(string)

    opposites = { "[" => "]", "{" => "}", "(" => ")", "]" => "[", "}" => "{", ")" => "(" }

        left = Array.new
        right = Array.new
        return_val = true

        split_array = string.split(//)
        split_array.delete_if { |e| e.match(/\s/) }

          split_array.each do |i| 
          if (i == "{" || i == "(" || i == "[")
              left.push(i)
            else (i == "}" || i == ")" || i == "]")
              right.push(i)
            end
          end

        # p left
        # p right

        left.each_index do |i|
          if left[i] != opposites[right[i]]
              return_val = false
          end
        end  
        return_val
    end 

    p valid_string?("[ ] } ]") == false
    p valid_string?("[ ]") == true
    p valid_string?("[  ") == false                 
    p valid_string?("[ ( text ) {} ]") == true    
    p valid_string?("[ ( text { ) } ]") == false  
    p valid_string?("[ (] {}") == false 
    p valid_string?("[ ( ) ") == false

-------Updated: After trying some different approaches, my refactor is this:-----------

def valid_string?(str)

    mirrored = { "[" => "]", "{" => "}", "(" => ")" }
    open_brackets = Array.new

    split_str_array = str.split("")

    split_str_array.each do |bracket| 
      if bracket.match(/[\[|\{|\(]/) then open_brackets.push(bracket)
      elsif bracket.match(/[\]|\}|\)]/)
        return false if mirrored[open_brackets.pop] != bracket
      end
    end
    open_brackets.empty?
end 

回答1:


My approach is as below :

def valid_string?(string)
  open_paren = ['[','{','(']
  close_paren = [']','}',')']
  open_close_hash = {"]"=>"[", "}"=>"{", ")"=>"("}
  stack = []
  regex = Regexp.union(close_paren+open_paren)
  string.scan(regex).each do |char|
    if open_paren.include? char
      stack.push(char)
    elsif close_paren.include? char
      pop_val = stack.pop
      return false if pop_val != open_close_hash[char]
    end
  end
  open_paren.none? { |paren| stack.include? paren }
end 

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

Algorithm :

  1. Declare a character stack S.
  2. Now traverse the expression string exp.
    • If the current character is a starting bracket (‘(‘ or ‘{‘ or ‘[') then push it to stack.
    • If the current character is a closing bracket (')' or '}' or ']') then pop from stack and if the popped character is the matching starting bracket then fine else parenthesis are not balanced.
  3. After complete traversal, if there is some starting bracket left in stack then “not balanced”



回答2:


The shortest regex solution is probably:

def valid_string? orig
  str = orig.dup
  re = /\([^\[\](){}]*\)|\[[^\[\](){}]*\]|\{[^\[\](){}]*\}/
  str[re] = '' while str[re]
  !str[/[\[\](){}]/]
end



回答3:


Another way:

s = str.gsub(/[^\{\}\[\]\(\)]/, '')
while s.gsub!(/\{\}|\[\]|\(\)/, ''); end
s.empty?

Ex 1
str = "(a ()bb [cc{cb (vv) x} c ]ss) "
s = str.gsub(/[^\{\}\[\]\(\)]/, '') #=> "(()[{()}])"
while s.gsub!(/\{\}|\[\]|\(\)/, '') do; end
  s => "([{}])" => "([])" => "()" => "" gsub!() => nil
s.empty? #=> true

Ex 2
str = "(a ()bb [cc{cb (vv) x] c }ss) "
s = str.gsub(/[^\{\}\[\]\(\)]/, '')  #=> "(()[{()]})"
while s.gsub!(/\{\}|\[\]|\(\)/, '') do; end
  s => "([{]})" gsub!() => nil 
s.empty? #=> false



回答4:


This should provide the same functionality

def valid_string?(string)
  #assume validity
  @valid = true
  #empty array will be populated inside the loop
  @open_characters = []
  #set up a hash to translate the open character to a closing character
  translate_open_closed = {"{" => "}","["=>"]","("=>")"}
  #create an array from the string loop through each item 
  string.split('').each do |e| 
    #adding it to the open_characters array if it is an opening character
    @open_characters << e if e=~ /[\[\{\(]/
    #if it is a closing character then translate the last open_character to 
    #a closing character and compare them to make sure characters are closed in order
    #the result of this comparison is applied to the valid variable
    @valid &= e ==  translate_open_closed[@open_characters.pop] if e=~ /[\]\}\)]/
  end
  #return validity and make sure all open characters have been matched
  @valid &= @open_characters.empty?
end

You could also do this with inject but it would be a bit less transparent.




回答5:


I was given this as part of a simulated interview coding challenge. In my case, there was also a parens map passed in { "(" => ")", "[" => "]" }, meaning types of parentheses could vary.

def balanced_parens(string, parens_map)
  # where we throw opening parens
  opening_parens = []
  i = 0
  while i < string.length
    # if current index is opening paren add to array
    if parens_map.keys.include? string[i]
      opening_parens << string[i]
    # if current index is closing paren, remove last item from opening_array 
    elsif parens_map.values.include? string[i]
      popped_paren = opening_parens.pop
      # checking that closing parens at current index is a match for last open parens in opening_array
      return false if string[i] != parens_map[popped_paren]
    end
    i += 1
  end
  # if opening_parens array is empty, all parens have been matched (&& value = true)
  opening_parens.empty?
end



回答6:


How about:

class Brackets
  def self.paired?(s)
    stack = []
    brackets = { '{' => '}', '[' => ']', '(' => ')' }

    s.each_char do |char|
      if brackets.key?(char)
        stack.push(char)
      elsif brackets.values.include?(char)
        return false if brackets.key(char) != stack.pop
      end
    end
    stack.empty?
  end
end


Brackets.paired?("[ ] } ]") # => false
Brackets.paired?("[ ]") # => true
Brackets.paired?("[  ") # => false
Brackets.paired?("[ (] {}") # => false
Brackets.paired?("[ ( ) ") # => false
Brackets.paired?("[ ( text { ) } ]") # => false
Brackets.paired?("[ ( text ) {} ]") # => true


来源:https://stackoverflow.com/questions/22122191/better-way-to-write-matching-balanced-parenthesis-program-in-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!